Does it matter we put the entity state = modified after changes or before making changes?
using (var db = new LakshyaContext())
{
foreach (var category in db.Categories)
{
db.Entry(category).State = EntityState.Modified; // before
category.Count = 25; //Making Changes
db.Entry(category).State = EntityState.Modified; //After
}
db.SaveChanges();
}
So first, let's get the most important thing out of the way:
You are right. In your example, you don't need to manually call
db.Entry(category).State = EntityState.Modified
. This is because you are loading the entries (categories) from the context above. This is known as the "Connected Scenario" where theDbContext
is aware of the entities, it's tracking them. This is the same, for instance in an ASP.NET Core app, where the context is shared across the HTTP request.Any modification you make between the scope of
using (var db = new LakshyaContext())
, will be known by the context when you callSaveChanges
.Now, when working on disconnected scenarios (as you said UnTracked entities), we have to dig a little bit deeper.
To understand that, first you need to know how the
DbContext
know what's changed. Take the following example:How does it know that the
Price
changed? since it's just a normal auto property on theBook
class? The magic lies behind theDetectChanges
method.In some specific cases, the
DbContext
calls theDetectChanges
method. The most obvious one is whenSaveChanges
is called. In a top level, the way it works is:DbContext
makes a snapshot of each entity it loadsSaveChanges
is called, it will proceed to callDetectChanges
which will do it's magic to figure it out what's changed or not.DbContext
then takes care of sending the correct commands to the db.At this point, we know the responsibility of
DetectChanges
. The important part now is knowing whenDetectChanges
is called (apart from SaveChanges that we already know). This is crucial to finally answer your "Order" question. From the linked article from Arthur VickersLet's examine this code that demonstrates the "disconnected" scenario.
When we go into the second
DbContext,
it is not aware of ourbook
entity. We change the price and then calldb.Entry(book).State = EntityState.Modified
. At this point, theDbContext
will start tracking it, andDetectChanges
is invoked. Proceeding callingSaveChanges
will work as expected.If we had done the opposite, calling
db.Entry(book).State = EntityState.Modified
before actually changing the price things would.... still work!Why? Well, manually changing the state of the entity with
db.Entry(book).State
will add the entity to the context, meaning it will start tracking it for changes. So, even if we calldb.Entry(book).State
and then apply changes on the entity it will not matter because callingSaveChanges
at the end, will trigger againDetectChanges
, and since it was already called before, there was already a snapshot in place for the entity.One way you can verify this behavior yourself is running the code above with logging enabled for the
DbContext
:Now some remarks:
The update above in the disconnected scenario will issue an update on ALL COLUMNS in the table. This might not be what you expected. There are ways to prevent this. Read more here
DetectChanges
does a lot of stuff internally, not only applying merges on changes. It takes care of Foreign Keys, updating references of navigation properties and more, and doing "fixup".More resources to read on: (especially the ones from Arthur Vickers!)
Secrets of DetectChanges Part 1: What does DetectChanges do?
Secrets of DetectChanges Part 2: When is DetectChanges called automatically?
Possible Issue with Change Tracker Caching Entity State EF Core 2.0.2
Working with Disconnected Entity Graph in Entity Framework Core
Entity Framework Core TrackGraph For Disconnected Data