Reputation: 9116
Using EntityFramework Core I have this code:
var theStudent = new Student();
theStudent.Title = "Mehran"
theStudent.Status = 1
mainDbContext.Set<Student>().Add(theStudent);
await mainDbContext.SaveChangesAsync();
// In reality data is changed by another program. To simulate it here I alter the data by another dbcontext and raw SQL
using (var utilDbContext = new MelkRadarDbContext())
{
var command = " update dbo.Student set status=2 where Id=@p0";
utilDbContext.Database.ExecuteSqlCommand(command, theStudent.Id);
}
var reloadedStudent = await mainDbContext.Set<Student>()
.Where(s => s.Id == theStudent.Id)
.FirstOrDefaultAsync();
Assert.AreNotEqual(reloadedStudent, student);
Assert.AreEqual(reloadedStudent.Status, 2);
Both of assertions are failed. It seems on the second call, mainDbContext
still returns the old theStudent
object as reloadedStudent
, and doesn't load it from the database to get the fresh data. Why is that so? What should I do to get the fresh data on the database?
Upvotes: 6
Views: 1874
Reputation: 18443
You have two options:
[Not Recommended] Use the Reload
or ReloadAsync
method for every entity:
await mainDbContext.Entry(theStudent).ReloadAsync();
Since this must be called for every entity separately, it is very inefficient when you need to reload a bunch of entities.
[Recommended] Create a new DbContext
. This is the ultimate way to solve the problem of stale data.
DbContext
s are designed to be short lived. They implement the Unit Of Work pattern, so it is recommended to create a DbContext
for every batch of related operation (a business transaction) - a user action for example (pressing the Save button). Although the common practice of having a single DbContext
for each HTTP request (in context of a web application or web service) satisfies this, but sometimes you need to perform more than one "batch of operation" in a request. That's the time you need to consider creating more DbContext
s.
The whole points of keeping a single DbContext
for a series of operations, are caching, tracking, and lazy loading. Whenever you need to reload the data, it is obvious that you don't need those features from that place onward. So it makes sense to use a new DbContext
.
A good question to answer is, why do you need fresh data in the first place? If you need to make critical decisions based on the data, and relying on stale data causes inconsistency in your data store, then even refreshing the entities does not help. In this situation, you need to use stronger mechanisms, like locks (database or otherwise) to protect against stale data.
Note: In Entity Framework 6 there is a Refresh
method that can be used to refresh all the objects at once. This method is not available in Entity Framework Core, as it din't prove to be all that useful.
Upvotes: 3
Reputation: 2318
The DbContext provides a first-level cache for objects that it is asked to retrieve from the data store. Subsequent requests for the same object will return the cached object instead of executing another database request.
the object is cached in the main context, and isn't updated yet, because you used different context when you updated the object, you can use AsNoTracking to ignore caching before loading, but for your case, the object is initialized then attached to the context (cached), what you can do is detach EntityState the object so the context isn't tracking the object anymore, then you can load it again, I hope this will work for you.
mainDbContext.Entry(theStudent).State = EntityState.Detached;
var reloadedStudent = await mainDbContext.Set<Student>()
.Where(s => s.Id == theStudent.Id)
.FirstOrDefaultAsync();
Upvotes: 0