Precompiling LINQ Queries in .NET Framework 4.5

While it is possible to precompile LINQ queries for Entity Framework either to have your queries run faster (either without parameters or with parameters) or to run your queries on a background thread, in .NET Framework 3.5/4, it’s neither pretty nor fun. The code isn’t particularly ugly but it does require some additional effort for you to have your queries precompiled. Unless you want to run your statement on a background thread, you’ll only benefit from precompiling if you execute the same LINQ statement many times (and, even then, if your queries aren’t very complex then you still may not get much benefit).

However, there is some benefit from precompiling and it would be nice to have it, provided you didn’t have to do any extra work. That’s where .NET Framework 4.5 steps in. While it’s always dangerous to talk about what’s supposed to be in an upcoming version of some software, the intent is for the framework to recycle your compiled LINQ queries (a feature called “auto-compiled LINQ queries”). Every time a LINQ statement is issued, the framework will build the statement’s expression tree (the first step in the compilation process) and generate a unique identifier for the statement. The framework will then search a cache to see if that identifier is already present. If the framework doesn’t find the identifier, the framework finishes the compilation process and adds the compiled version of the statement to the cache before executing it; if the framework finds the identifier, it uses the already compiled version of the statement.

Notice what’s happening here: You still have to pay for the statement’s expression tree to be generated but, once you’ve paid that cost, if your statement or one very much like it already exists in the cache then all subsequent compile steps are skipped and your statement is just executed. If you have the identical LINQ statement in several places in your application then there’s no need—from an efficiency point of view—to rewrite your application to have that LINQ statement appear only once. The cache will store only one version of the statement and use it from all the places it appears in your application (of course, you’d want to have a good reason for scattering an identical LINQ expression throughout your code). As an added benefit, statements are only compiled if they are used. The older precompile method meant that you could precompile a LINQ statement that you might never use.

You don’t have access to the compiled queries in the cache (at least, not yet) so you’ll need to continue using CompiledQuery if you want to execute your LINQ statement on a background thread.

Turning Off Auto-Compile

If you don’t want to have your statements precompiled, you can turn the feature off by setting the DefaultQueryPlanCachingSetting on the ObjectContext’s ContextOptions property to false, like this:

oc.ContextOptions.DefaultQueryPlanCachingSetting = false;

I can only see two scenarios when turning off this option would be a good thing. Turning off auto-compile would eliminate whatever cost is involved in generating the identifier and walking the cache but, I’m willing to assume, that will be more than offset by not compiling a LINQ statement more than once. If you have a LINQ statement which is only executed once (and, furthermore, if this is true of all the LINQ statements associated with this instance of the ObjectContext) then setting this option to false would probably be a good choice. In this scenario, there’s also no benefit in using the CompiledQuery class to precompile your queries.

The second scenario involves sticking with the CompiledQuery class. Because there is a cost associated with maintaining and searching the cache, using the CompiledQuery class should give you better performance than the auto-compile option—no need to generate an identifier or maintain the cache (provided, of course, you don’t precompile statements you never use). If you’re obsessed with performance and don’t mind the extra work (or are paid by the hour) you could stick with the CompiledQuery class, even in .NET Framework 4.5. In this scenario, you may need to turn off DefaultQueryPlanCachingSetting to prevent the framework from also maintaining its cache of auto-compiled statements.

One last note: This does mean that if you’re using the CompiledQuery class in your existing code then, when you upgrade to .NET Framework 4.5, you’ll actually be getting better performance than the programmers who count on the framework’s default behaviour.