RSS

Linq Entity Caching

05 Jan

Okay, so here is a very brief example of caching a linq entity and the big gotcha. Below is a very simple example of a FindById function with caching.  We are using are using Velocity/AppFabric for our base cache manager and it is wrapped up with some custom code to aid in the “gets” and “puts”. There are a different ways to approach caching with linq (e.g. caching the query, results of query, etc.) but in this simple case we get the cache key as the only parameter on the function so we’ll just do a dead simple implementation of caching the result of the query. When I first looked to implement caching on this guy I thought “hey this will be super easy and will provide a lot of performance gain!”  So the first try looked like this.

public static Item FindById(Guid id)
{
    Item item = (Item)DataCache.Get(id, Region.LinqItemManager);
    if (item == null)
    {
        item = DataContext.Items.Where(i => i.ID == id).FirstOrDefault();
        DataCache.Put(id, item, Region.LinqItemManager);
    }
    return item;
}

“Man that was easy! So clean and simple, man I’m a good dev! Test it? What? Why? That is some good stuff right there!…..Oh, I didn’t see that coming…”

As it turned out in the DBML Item has many properties and a few associations, one of which is an association to Catalog. Well during a certain code path when we call FindById() and cache that Item, we never referenced Item.Catalog. On a subsequent call to FindById() for the same ID we pulled the cached version  and tried to reference Item.Catalog and it was null. Okay I seem to remember something about serializing a linq entity will cause it to become disconnected from its original context. And that makes a lot of sense in a web app where the context only exists for the life of the request but the linq object is living in a cache. Okay, so I need to re-connect it to a context so the associations will work again.  Attach() is your friend, but your friend is a snob.

public static Item FindById(Guid id)
{
    Item item = (Item)DataCache.Get(id, Region.LinqItemManager);
    if (item == null)
    {
        item = DataContext.Items.Where(i => i.ID == id).FirstOrDefault();
        DataCache.Put(id, item, Region.LinqItemManager);
    }
    else
    {
        DataContext.Items.Attach(item);
    }
    return item;
}

Cause that doesn’t work. You’ll get all kinds of exceptions. Like “Cannot add an entity with a key that is already in use” or “Attach or Add an entity that is not new, perhaps having been loaded from another DataContext.”  The answer: If you don’t have a “fresh” context you will continue to get exceptions like those. So just get a new context instance and it works just fine.

public static Item FindById(Guid id)
{
    Item item = (Item)DataCache.Get(id, Region.LinqItemManager);
    if (item == null)
    {
        item = DataContext.Items.Where(i => i.ID == id).FirstOrDefault();
        DataCache.Put(id, item, Region.LinqItemManager);
    }
    else
    {
        WebDataClassesDataContext context = new WebDataClassesDataContext();
        context.Items.Attach(item);
    }
    return item;
}

Cheers,
Joe

Advertisements
 
Leave a comment

Posted by on January 5, 2010 in Development

 

Tags: ,

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

 
%d bloggers like this: