1

Can someone please explain to me why the Entity Framework 6 cache works the way it does? It makes no sense to me.

As I read it (and experienced), Ef will always query the database - but if the data already exists in the context, then it will use the old data. Why would you do that? Surely this is just creating unnecessary database reads. If you are going to get the data, why would you not always use the most recent version.

https://learn.microsoft.com/en-us/ef/ef6/querying/

When results are returned from the database, objects that do not exist in the context are attached to the context. If an object is already in the context, the existing object is returned (the current and original values of the object's properties in the entry are not overwritten with database values).

Is there a way to get EF6 to update its context when it makes these queries?

EDIT: The reason why refreshing the context is not the solution for me is that it would require several other queries to run for the sake of a single small query. Basically the context is used to fetch several thousand records. The data may change in the background because I have other operations running async in a different context.

I have work arounds, I am just trying to understand why there isnt an easy option to update the data in the context with the information retrieved in a subsequent Where query.

Taking this operation:

_context.Products.Where(x=>categoryid==_categoryid);
var p = _context2.Products.FirstOrDefault(x=>x.ProjectId==1);
p.Description = "New Description";
_context2.SaveChanges();

Now if I run this query

_context.Products.Where(x=>categoryid==_categoryid);

Entity framework will get a data set in which it knows the latest value for product 1 ("New Description"), but it will totally ignore it and just return the value from the original context.

You can:

  1. Use DbEntry.Reload() - but that requires you to get the entity first, and then reload it - 2 trips
  2. New context (not a good option for me here)

Is there an option that would do something like the following pseudocode:

_context.BeginUpdateCache();
_context.Products.Where(x=>categoryid==_categoryid);
_context.EndUpdateCache();

which would result in _context.Products.Where(x=>categoryid==_categoryid) returning a set with the latest database value for product 1 - e.g. ("New Description")

statler
  • 1,322
  • 2
  • 15
  • 24
  • The easiest way to force a full refresh is to recreate the context: https://stackoverflow.com/questions/22177491/how-to-force-entity-framework-to-always-get-updated-data-from-the-database – IMil Jul 21 '20 at 01:45
  • Yes, but this is not an efficient option for me. Refer edit – statler Jul 21 '20 at 04:41

1 Answers1

1

Please refer Tracking vs. No-Tracking Queries & Automatic detect changes for detailed information. I am explaining here in brief.

You can explicitly say context to not track entities with AsNoTracking() extension method. It will be applied on entity level. You can use it like below.

_context.Products.AsNoTracking().Where(x=>categoryid==_categoryid);
var p = _context2.Products.FirstOrDefault(x=>x.ProjectId==1);
p.Description = "New Description";
_context2.SaveChanges();
_context.Products.AsNoTracking().Where(x=>categoryid==_categoryid); //<- this will return latest object.

Or if you want to implement this on context level then set it as below.

_context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

Your complete code would be like below.

// Add below line.
_context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
_context.Products.Where(x=>categoryid==_categoryid);
var p = _context2.Products.FirstOrDefault(x=>x.ProjectId==1);
p.Description = "New Description";
_context2.SaveChanges();
_context.Products.Where(x=>categoryid==_categoryid);

Note As per your scenario do not use AsNoTracking() or QueryTrackingBehavior.NoTracking on _context2. Otherwise it will not track entity from FirstOrDefault(x=>x.ProjectId==1) and your _context2.SaveChanges(); will have no changes to save.


Edit As per your comment I guess you want to update the Entity also from this context. In this case you can use DbSet.Attach(Object) method as explained below.

var p = _context.Products.AsNoTracking().Where(x=>categoryid==_categoryid);
_context.Products.Attach(p);
p.Description = "New Description";
_context.SaveChanges();
_context.Products.AsNoTracking().Where(x=>categoryid==_categoryid); //<- this will return latest object saved in database.
Karan
  • 12,059
  • 3
  • 24
  • 40
  • Very concisely explained. Thank you very much. I have marked it as the answer, but strictly it doesn't quite give a complete answer. Could you advise if there is any way to force the original context to use the latest data it retrieves? e.g. like _context.Products.AsNoTracking().Where(x=>categoryid==_categoryid); except that not only is the latest value returned, but also updated in the context? – statler Jul 22 '20 at 06:42