Development With A Dot

Blog on development in general, and specifically on .NET

Sponsors

News

My Friends

My Links

Permanent Posts

Portuguese Communities

Entity Framework Code First: Get Entities From Local Cache or the Database

Entity Framework Code First makes it very easy to access local (first level) cache: you just access the DbSet<T>.Local property. This way, no query is sent to the database, only performed in already loaded entities.

If you want to first search local cache, then the database, if no entries are found, you can use this extension method:

   1: public static class DbContextExtensions
   2: {
   3:     public static IQueryable<T> LocalOrDatabase<T>(this DbContext context, Expression<Func<T, Boolean>> expression) where T : class
   4:     {
   5:         IEnumerable<T> localResults = context.Set<T>().Local.Where(expression.Compile());
   6:  
   7:         if (localResults.Any() == true)
   8:         {
   9:             return (localResults.AsQueryable());
  10:         }
  11:  
  12:         IQueryable<T> databaseResults = context.Set<T>().Where(expression);
  13:  
  14:         return (databaseResults);
  15:     }
  16: }

Comments

SamZ said:

nice. I'll try to use it.

# March 19, 2012 9:54 AM

brianhartung said:

It would be nice if you could cache the manually compiled expressions to avoid that expense each time.  Just not sure if GetHashCode on Expression would be sufficient or if we'd have to pay a hefty reflection price each time just to build up enough of a hash key to identify the expression (thereby rendering the point moot).

# March 19, 2012 10:11 AM

Ricardo Peres said:

@brianhartung:

Yes, I think it can be done, by introducing a static readonly ConcurrentDictionary<Expression, Delegate> field.

# March 19, 2012 10:35 AM

brianhartung said:

If from the call site I use literal expressions:

var customer1 = context.LocalOrDatabase<Customer>(c => c.Id==1);

var customer2 = context.LocalOrDatabase<Customer>(c => c.Id==1);

I'm not sure the dictionary lookup would recognize the latter as the same expression.  Obviously they solved this same problem in order to implement cached queries in EF itself so a peek into that code would probably be instructive (either that or perhaps you'll tell me I'm a git and am making this more complicated than it is :)

# March 19, 2012 11:24 AM

RichardD said:

@brianhartung:

You're right - expressions use reference equality, so in your example the two expressions are not considered equal.

If you're just looking to retrieve the entity by its primary key, you might want to look at the Find method introduced in 4.1:

"If an entity with the given primary key values exists in the context, then it is returned immediately without making a request to the store. Otherwise, a request is made to the store for an entity with the given primary key values and this entity, if found, is attached to the context and returned. If no entity is found in the context or the store, then null is returned."

var customer1 = context.Customers.Find(1);

var customer2 = context.Customers.Find(1);

blogs.msdn.com/.../using-dbcontext-in-ef-feature-ctp5-part-3-finding-entities.aspx

# March 19, 2012 4:18 PM

brianhartung said:

Hi Richard...yep, I am familiar with the Find method...I was just using the first simple predicate that came to mind.  So I suppose the larger question is if we can find a way to perform transparent second-level caching of compiled expressions that would avoid paying the (relatively speaking) heavy cost of compilation (that we're trying to avoid in the first place) just to do the lookup in our cache structure.

Cheers,

-Brian

# March 19, 2012 4:56 PM

brianhartung said:

Not sure why I didn't spend the 30 seconds to look on StackOverflow for this in the first place... :)

stackoverflow.com/.../when-an-expressiont-is-compiled-is-it-implicitly-cached

# March 19, 2012 4:59 PM