All Articles

EF5: Entity Framework cache problems

The majority of ORMs supports a first-level cache (L1), common named as "identity cache". Basically, if we request the same entity twice  by its ID, it doesn't require a new round-trip to the database. EF5 is no exception, and provide us with the method Find() on the DbSet<T> class. From the documentation we have that Find

Uses the primary key value to attempt to find an entity tracked by the context. If the entity is not in the context then a query will be executed and evaluated against the data in the data source, and null is returned if the entity is not found in the context or in the data source. Note that the Find also returns entities that have been added to the context but have not yet been saved to the database.

Given that, if I make the call

db.Contacts.Find(id);

I know what is the expected behavior. The main problem exists with this code below

//Get the contact for Id=1
var contact = db.Contacts.First(x => x.ContactId == 1)
Console.WriteLine("The Current Name is: " + contact.Name);

//Update the contact in the same session without using the context tracking
db.Database.ExecuteSqlCommand("update dbo.Contacts set Name = 'Filipe' where ContactId = 1");

//Get the contact again
var updatedContact = db.Contacts.First(x => x.ContactId == 1);
Console.WriteLine("The Updated Name is: " + updatedContact.Name);

Assuming that the initial name is André, what do you think is the output of this code? Here is the output

To be honest, this is not what I expected to see. I was expecting to see André followed by Filipe.

After looking at the code, I thought: well EF is smart enough to see that I am using the same query twice, 

db.Contacts.First(x => x.ContactId == 1)

with the same params, in the same session, and is caching the data, then the 2nd call does not go to the database. Cool, I thought.

Let's confirm with the SQL profiler, expecting to see just one query

I was totally wrong. The EF goes to database twice, getting the contact by Id. However, EF does not refresh the entity that is in context. Maybe this a a by design decision, to not sacrifice performance, since it would be required to merge the entity that is currently in context with the entity fetched from the database. However, in my opinion, it's hard to accept the fact that EF goes to the store to fetch the entity, and does nothing with it if the entity already exists in context. 

To get the updated entity in context, we have two options :
1) detach the entity before querying it again
((IObjectContextAdapter)db).ObjectContext.Detach(contact);
2) Refresh the entity
((IObjectContextAdapter)db).ObjectContext.Refresh(System.Data.Objects.RefreshMode.StoreWins, contact);

If you opt for the Refresh option, it is not necessary to query again. Here is the final code
//get the contact
var contact = db.Contacts.First(x => x.ContactId == 1)
Console.WriteLine("The Current Name is: " + contact.Name);

//update using ExecuteSqlCommand
db.Database.ExecuteSqlCommand("update dbo.Contacts set Name = 'Filipe' where ContactId = 1");

//Refresh the entity
((IObjectContextAdapter)db).ObjectContext.Refresh(System.Data.Objects.RefreshMode.StoreWins, contact);
Console.WriteLine("The Updated Name is: " + contact.Name);

Here is the ouput

This a warning to all readers that can have this situation in a project, for example, when in the same context execute a stored procedure that modify entities that are already loaded in the current EF context, and for some reason you need to refresh the entities. Do not assume that by seeing the statement in the SQL profiler that EF will merge the data in context.


Published Dec 3, 2012

Cloud Solutions and Software Engineer. Married and father of two sons. Obsessed to be a continuous learner.