Project Description
Easy compiled queries for Entity Framework and Linq to SQL.

You no longer have to declare static Func's to hold the compiled queries. Even better, you no longer need to figure out the right generic parameters for the Func's. Instead, QueryCache infers them automatically.

For instance, instead of writing this

        // setup...
        static Func<NorthwindEntities, string, string, IQueryable<Customer>> _customerQuery = 
            CompiledQuery.Compile<NorthwindEntities, string, string, IQueryable<Customer>>(
                (ctx, name, postcode) =>

                    from c in ctx.Customers
                    where c.City == name &&
                          c.Postcode == postcode
                    select c);

        //  .... and then use it ...

       var customers =  _customerQuery(context, cityName, postcode)

You can just write this, all at the site of usage:

   var customers=  context.Pass(new { cityName, postcode })
                          .ToCompiledQuery<Customer>(() => (ctx, p) =>

                          from c in ctx.Customers
                          where c.City == p.cityName &&
                                c.Postal_Code == p.postcode
                          select c);

Note that:
  1. You didn't have to provide a whole lot of generic type params. Instead, you just have to provide one, and it's an easy one (<Customer> in the example above).
  2. You didn't have to remember which order the parameters are in. With the normal approach (top) you get no useful intellisense at the site of usage. You just have to remember the order of the parameters. With QueryCache, the parameters are named, because they are members of the anonymous type that you create on the first line. ( "new {cityName, postcode}" in the example above). In the query, you use them by name (e.g. p.cityName in the query above)
  3. Your query appears at the site of usage (so you don't need to remember "which static Func does what?")
  4. You don't even have to define statics. Instead, QueryCache manages the lifetimes of the compiled queries for you.
  5. QueryCache is fast. Compared to the "normal" way of doing things, the extra per-call overhead of using QueryCache is just one lookup into a ConcurrentDictionary.
  6. QueryCache is implemented in a single cs file, which you can just drop into your project. It's only 145 lines of code, most of which are actually comments.

NB: QueryCache caches your compiled queries. It does not cache the results.

The lambda expression that defines your query is only evaluated on the first call. When that call is made, the lamdba is evaluated, the resulting query is compiled, and the compiled query is cached. Subsequent calls just grab the compiled query straight from the cache.

UPDATE July 2011

Please note that Microsoft have just released, in CTP form, automatic compilation of EF queries. That will be a much better option than QueryCache, and I recommend it.

Entity Framework or LINQ to SQL?

In theory, QueryCache will work fine with both. There are just two "usings" at the top of QueryCache.cs which you need to change to switch is between the two different technologies. But pls bear in mind that I have not yet had time to test it with LINQ to SQL(!!)

Limitations:

  • Currently there is only one cache. (In a future release, the API may be extended to allow you to allocate different queries to difference instances of QueryCache (e.g. to control merge options by running different caches for different merge options)).
  • You should consider the cache to be a per-usage-site cache. This is because the key in the cache is the anonymous delegate (Func) that is passed to "ToCompiledQuery". If you want to make the same query from several places in the code, define one method to do the querying, so that there is only one anonymous func that defines the query. (You can see an example of this pattern in the QueryCache tests, where the method is named GetCustomers ).
  • Because it uses a ConcurrentDictionary, QueryCache requires .NET version 4.
  • Yes, the line that looks like this is ugly "ToCompiledQuery<Customer>(() => (ctx, p) =>". The last bit means "a function that returns an expression that takes two parameters". Sorry its so ugly ;-) Fortunately, you can just copy and paste it, changing <Customer> to your entity type.

Maturity

QueryCache is just an idea I had recently. As at initial release on Codeplex (April 2011) I have not used it in production. Having said that, the code is very simple, so you can check it yourself if you feel so inclined ;-)

Last edited Jul 4, 2011 at 8:02 AM by johnrusk, version 21