Oops, we did it again

A new update to LINQ preview is available at MSDN site.

What's the coolest new feature?  IMHO, its IQueryable<T>. 

 DLINQ's query mechanism has been generalized and available for all to use as part of System.Query.  It implements the Standard Query Operators for you using expression nodes to represent the query. Your queries can now be truly polymorphic, written over a common abstraction and translated into the target environment only when you need it to.

  public int CustomersInLondon(IQueryable<Customer> customers) {

        int count = (from c in customers

                     where c.City == "London"

                     select c).Count();

        return count;

    }

Now you can define a function like this and it can operate on either an in memory collection or a remote DLINQ collection (or you own IQueryable for that matter.)  The query is then either run entirely locally or remotely depending on the target. 

If its a DLINQ query a count query is sent to the database.

SELECT COUNT(*) AS [value]

FROM [Customers] AS [t0]

WHERE [t0].[City] = @p0

If its a normal CLR collection, the query is executed locally, using the System.Query.Sequence classes definitions of the standard query operators.  All you need to do is turn your IEnumerable<Customer> into IQueryable<Customer>.  This is accomplished easily with a built-in ToQueryable() method.

  List<Customer> customers = ...;

  CustomersInLondon(customers.ToQueryable());

Wow!  That was easy.  But, how is this done?  How can you possible turn my List<T> into some queryable thingamabob?

Good question.  Glad you asked.

Check out this little gem: 

  Expression<Func<Customer,bool>> predicate = c => c.City == "London";

  Func<Customer,bool> d = predicate.Compile();

 

Now you can compile lambda expressions directly into IL at runtime!

ToQueryable() wraps your IEnumerable<T> in IQueryable<T> clothing, uses the Queryable infrastructure to let you build up your own expression tree queries, and then when you enumerate it, the expression is rebound to refer to your IEnumerable<T> directly, the operators rebound to refer to System.Query.Sequence, and the resulting code is compiled using the built-in expression compiler.  That code is then invoked producing your results.

Amazing, but true.

But wait, there is more!  Yes, it is true, not only can you slice, but you can also dice!

That's not the end of IQueryable<T>'s little bag of tricks.  What else could there be? How about dynamic queries, ones you can build up at runtime? Yes? No?  Say it isn't so!

It's true.  IQueryable<T> is fully dynamic.  Sort of.  Each IQueryable<T> has a method CreateQuery<S>() that creates a new IQueryable<S> (of the same concrete type) with a single Expression tree argument.  That is, if I have an expression tree representing the code that defines a query, I can make an IQueryable<S> out of an old IQueryable<T>.

That's great you say, but you probably don't statically know what the 'T' and 'S' are in your program if you are building dynamic queries at runtime.  True, true, we've thought of that too. You see, IQueryable<T> has a smaller cousin, IQueryable, just like IEnumerable<T> has its little friend IEnumerable.  IQueryable is non-generic, but its ever so much a query as IQueryable<T>.  In fact, it has its own CreateQuery() method that builds a new IQueryable so you can do all this query building without all that static info getting in your way.

So how do you build the expression trees that make up a query.  Check out the System.Query.QueryExpression class.  It is a collection of static methods that correspond to the standard query operator pattern, but are more loosely typed, taking arguments that are Expression's instead of Expression<T>'s.  These methods do all the work necessary to build up the call nodes that represent calls to the more fully typed IQueryable<T> query operator methods. 

Here's how you can dynamic build up the query in the prior example.

IQueryable q = ...;

ParameterExpression p = Expression.Parameter(typeof(Customer), "c");

Expression body = Expression.EQ(

    Expression.Property(p, typeof(Customer).GetProperty("City")),

    Expression.Constant("London")

    );

LambdaExpression predicate = QueryExpression.Lambda(body, p);

Expression where = QueryExpression.Where(q.Expression, predicate);

q = q.CreateQuery(where);

 

You can then call GetEnumerator() on 'q' to execute the query.

Too hard you say?  Do I have to write all that code?  What if I just want my users to type in a little filter expression.  Do I have to turn that into all those nodes?

Ah, well, we've thought of that too.  Try this out. 

IQueryable q = ...;

ParameterExpression p = Expression.Parameter(typeof(Customer), "c");

LambdaExpression predicate =
QueryExpression.Lambda("c.City = 'London'", p);

Expression where = QueryExpression.Where(q.Expression, predicate);

q = q.CreateQuery(where);

Or if you don't want them to have to type silly dot's, just don't give the parameter a name.

IQueryable q = ...;

ParameterExpression p = Expression.Parameter(typeof(Customer), "");

LambdaExpression predicate =
QueryExpression.Lambda("City = 'London'", p);

Expression where = QueryExpression.Where(q.Expression, predicate);

q = q.CreateQuery(where);

 

Viola!  Instant filter. Instant dynamic query. Run it here. Run it there. Why, you can run it everywhere.

But that's enough from me. Why are you wasting time reading this when you could be downloading the preview and trying it out for yourself?

Matt