When you write a LINQ to Entities query today, the Entity Framework walks over the expression tree generated by the C#/VB compiler and translates (or compiles) that into SQL.
Compiling the expression tree into SQL involves some overhead though, particularly for more complex queries. In order to avoid having to pay this performance penalty every time the LINQ query is executed, the CompiledQuery class was introduced. This allows you to pay the overhead of compilation just once, and gives you back a delegate that points directly at the compiled version of the query in the Entity Framework cache.
static readonly Func<NorthwindEntities, IQueryable<string>> query =
db => from c in db.Customers
where c.Country == "USA"
Each subsequent time you execute the query it’s now much faster.
Auto-Compiled LINQ to Entities Queries
The Entity Framework June 2011 CTP supports a new feature called Auto-Compiled LINQ Queries. Now every LINQ to Entities query that you execute automatically gets compiled and placed in EF’s query cache. Each additional time you run the query, EF will find it in its query cache and won’t have to go through the whole compilation process again.
This also provides a boost to queries issued using WCF Data Services, as it uses LINQ under the covers.
How Does it Work?
The Entity Framework will walk the nodes in the expression tree and create a hash which becomes the key used to place it in the query cache. If it does not find the query in the cache then it will go ahead and compile it and store the compiled query in the cache for subsequent use. Each subsequent time, the hash will be calculated and find the compiled query in the tree, thus saving the compilation overhead.
Turning off Query Caching
A new property on ObjectContext.ContextOptions has been introduced that allows you to control the default behavior of query compilation. By default this property is true, but you can set it to false to disable auto-compilation of LINQ queries:
db.ContextOptions.DefaultQueryPlanCachingSetting = false;
Note: If you’re using DbContext and need to get the ObjectContext instance, just cast to IObjectContextAdapter:
((IObjectContextAdapter)db).ObjectContext.ContextOptions.DefaultQueryPlanCachingSetting = false;
The value of this property gets propagated to the EnablePlanCaching property on each newly-created ObjectQuery for the ObjectContext. Note: There is a known limitation in the June 2011 CTP: while DefaultQueryPlanCachingSetting is propagated to the EnablePlanCaching setting of each new query, the EnablePlanCaching setting is not propagated when using LINQ operators.
Obviously when it comes to performance there’s no one-size-fits-all approach. The size/complexity of your application and queries will greatly influence how much of a performance boost your application will get from this. It’s important to measure your specific application with a good profiler.
It is worth noting that using the CompiledQuery class is still the fastest approach, as the delegate that you get back does not need to calculate a hash of the expression tree. Auto-compiled LINQ queries should be almost as fast though, and provide the benefit of not having to manually use the CompiledQuery class.
We’d love to get your feedback on this feature and hear if it’s helping in your application. Please download the CTP today and let us know what you think!
Entity Framework Team