Compiled Queries and the EntityFramework

For a while LINQ to SQL has had compiled Queries... and now in Beta3 of the Entity Framework, due out soon, we have them too.

The idea behind compiled queries is that you don't want to re-process the same LINQ expression every time it is encountered. Instead you want to pre-compile the LINQ expression, so that the each time it is executed we can simply fold in parameters to a pre-compiled command tree.

The way you do this looks pretty simple:

var ordersAfterDate = CompiledQuery.Compile( (NorthwindEntities ctx, DateTime date) =>
            from order in ctx.Orders
            where order.OrderDate > date
            select order
);

CompiledQuery.Compile(..) works by taking in an Expression<Func<TArg0,TResult>>, or one of 3 other overloads that allow for a maximum of 4 input parameters.

In the above example I am using the overload that has 2 input parameters i.e. Expression<Func<NorthwindEntities ,DateTime,IQueryable<Order>>.

What this returns is simply a Func<NorthwindEntities ,DateTime,IQueryable<Order>>. I.e. we get back a delegate we can call.

Here anonymous types and the var keyword save us a lot of hassle, see we don't need to declare our compiled query: Func<NorthwindEntities ,DateTime,IQueryable<Order>> a simple var will do. Which is just as well cause otherwise our code would start to look plain old ugly.

Now, if you take a look under the covers you will see that each of the overloads has a constraint on TArg0 (where TArg0: ObjectContext ) that means you must always pass an ObjectContext , or a specialized ObjectContext like my NorthwindEntities , as the first parameter.

It is done like this so you can create your compiled queries outside of the scope of a ObjectContext and share them amongst threads etc, which is just as well because otherwise the recommended pattern of creating ObjectContexts when you need them would make compiling queries next to useless in ASP.NET scenarios.

I can hear you saying "Yeah yeah enough with the detail already, how do I actually use them?"

Well actually it couldn't be simpler, it is just a delegate and you invoke it like any other delegate, all you need to do is pass it the arguments it expects, intellisense will guide you all the way!

using (NorthwindEntities ctx = new NorthwindEntities())
{

    DateTime before = DateTime.Now.AddYears(-20);
foreach (Orders order in ordersAfterDate(ctx,before))
Console.WriteLine(order.OrderID);

}

Cool huh?

NB: Don't think that TResult needs to be IQueryable<Order>> or some other EntityType you could just as easily create projections that use anonymous types.