Monty’s Gush

FromCache now supports local collections

Posted on: January 3, 2011

A commenter recently discovered that the technique I use to generate a cache key for any IQueryable was not working properly for one of his queries. The query contained a call to List.Contains, and the contents of the list was not getting put into the cache key.

The problem was that the query cache didn’t specifically know about local collection values.

Local collection values can be supplied as parameters to query operators such as Contains and Any, if the query provider supports them.

var ids = new List<int> { 1, 3, 5 };

var q = from c in db.Customers
        where ids.Contains(c.CustomerId)
        select c;

As far as FromCache was concerned, ids is just a constant expression, and treated it no differently to any other constant when evaluating its cache key.

Unfortunately, the ToString implementation of Lists (and of Arrays, and IEnumerables in general) is not suitable for use as a cache key, because it outputs the type name and not (for example) a representation of every element in the collection.

I couldn’t understand how I’d overlooked this. But then I remembered that Entity Framework v1, which was the main provider I was using at the time, didn’t support local collections either!

I’d actually written my own WhereAny and WhereAll methods, which build up expressions from local collections and produce queries that Entity Framework v1 could understand. But that post never made it out of my WordPress drafts. :-)

LINQ to SQL has always supported the Contains method, and Entity Framework now supports both Any and Contains overloads with local collections. So, I’ve updated the original source code and the blog post to support local collections.

This involves a pass over the expression tree to expand appropriate local collection values which might be used in methods like Any or Contains. Each method call in the expression tree is examined, and the argument to any parameters of type IEnumerable<> or List<> is wrapped in an object with an implementation of ToString which prints every element in the collection.

I’ve also extracted the method IQueryable.GetCacheKey and made it public, which could help emphasise that the meat of the idea is to automatically generate a cache key for a query (and that you can do whatever you like with it, such as make a FromCache extension method, or managing the cache manually).

If you’re using the FromCache extension method, please update use the latest version of the source code!

.NET 4 methods are now included in the source for those of you using 3.5.

kick it on DotNetKicks.com

12 Responses to "FromCache now supports local collections"

I fixed this issue by updating the SubtreeEvaluator.Evaluate function. It looks for arrays and enumberables and rewrites them to a NewArrayExpression which ToString is what we’re expecting. Source code is in PLINQO and in the following form post …

http://community.codesmithtools.com/Template_Frameworks/f/66/p/11653/44664.aspx#44664

I found this on a Silverlight site can anyone change this into an extension method?

///
/// Creates a new expression that is like this one, but using the
/// supplied children. If all of the children are the same, it will
/// return this expression.
///
/// The property of the result.
/// The property of the result.
/// This expression if no children changed, or an expression with the updated children.
public MethodCallExpression Update(Expression @object, IEnumerable arguments) {
if (@object == Object && arguments == Arguments) {
return this;
}
return Expression.Call(@object, Method, arguments);
}

Hiya Paul, I’ve added the .NET 4 methods to the source article for you. I’ve not tested them but let me know if there are any probs.

awesome. Testing now

Just ran it through my sanity test suite and all tests are green. I don’t quite understand what the difference is but the code written for PLINQ won’t pass my test suite. I run my full regression set overnight and report back.

I’ll also test also test wether a deferred query used as the parameter for the Contains() get expanded correclty

Ok its all good news for me. All 1694 of my integration tests run correctly. I’ve also removed a ToList() that materialized a query which was used in a following query.

Now a couple more questions. I’m using this cache for a read only database. Can I safely set the sliding window expiry time to say 24 hrs?
I’m also struggling to find some perfmon counters to monitor my cache hit/misses. Can anyone point me to the right ones?

And finally a big THANKS

I don’t know if this is a bug or whether I have done something wrong but If I have created a query like this…

public IQueryable All() where T : class, new()
{
return new ObjectQuery(GetSetName(), this.context, MergeOption.NoTracking).FromCache().AsQueryable();
}

And run the query against the AdventureWorks database like this….

List products = new List(session.All().Where(x => x.Color.Equals(“Black”, StringComparison.InvariantCultureIgnoreCase)));

Then I will get a nullExceptionError since some products Color property is null.

A workaround is this…..

List products = new List(session.All().Where(x => x.Color != null && x.Color.Equals(“Black”, StringComparison.InvariantCultureIgnoreCase)));

but obviously that isn’t really acceptable as a standard query with the following code would allow me to return a list without the exception and workaround.

public IQueryable All() where T : class, new()
{
return this.context.CreateQuery(this.GetSetName()).AsQueryable();
}

Any ideas?….

Many Thanks

James

Hiya James, sorry I’ve literally got no idea what you’re up to… try using == instead of .Equals?

Pete

Thanks for the swift reply! I’m sorry I should have explained better.

I’ve been working on a way to utilize the Entity Framework using generics so the code I posted was part of something much larger.

Using == works though I’m not sure why. I would have assumed .Equals() would have used == internally. I’ll have to see if I can come up with something that won’t cause the error.

Running a few tests on your caching framework shows some outstanding results. I’m able to return results so much faster.

It’s oustanding stuff!

No, that’s not how == and Equals work… :-) In particular, == is a static method whereas Equals is an instance method, and you cannot invoke an instance method on a null object reference in C#.

Thanks… Yeah after a bit of reading I figured that. I’ve managed to get it all working now though so all is good.

I’m amazed by the results. Incredible work!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: