The ChangeTracker is the internal brain of Entity Framework Core. When you call SaveChanges(), EF Core doesn't inspect the database; it inspects the ChangeTracker. The tracker assigns a specific EntityState (Added, Modified, Deleted, Unchanged, Detached) to every object it manages.
The SQL that gets generated is 100% determined by the object's current state.
| EntityState | When it happens | SaveChanges Output |
|---|---|---|
Detached | The object was created locally, or queried with AsNoTracking. | Ignored. No SQL executed. |
Unchanged | The object was queried, but no C# properties were altered. | Ignored. No SQL executed. |
Added | You called _context.Add(). | INSERT INTO statement. |
Modified | You altered a property, or called _context.Update(). | UPDATE statement. |
Deleted | You called _context.Remove(). | DELETE FROM statement. |
You can debug exactly what EF Core is planning to do by looking directly into the ChangeTracker.
var user = await _context.Users.FindAsync(1); // State = Unchanged
user.Name = "Sandeep"; // State instantly becomes 'Modified'
// Manually verify the state:
var trackingInfo = _context.Entry(user);
Console.WriteLine(trackingInfo.State); // Prints: Modified
// See exactly WHICH properties changed!
var nameProp = trackingInfo.Property(u => u.Name);
Console.WriteLine($"Original: {nameProp.OriginalValue}, Current: {nameProp.CurrentValue}");
You don't have to rely on automatic tracking. You can forcefully dictate the state of an object to bypass EF Core's built-in change detection.
var user = new User { Id = 1, Name = "Force Update" };
// Attach the object and violently FORCE its state to Modified
_context.Entry(user).State = EntityState.Modified;
// Generates an UPDATE statement for ALL columns, because it assumes the whole object is altered
await _context.SaveChangesAsync();
Q: "We queried a User object. We need to update their `DateOfBirth`, but we DO NOT want to update their `PasswordHash`, even though a rogue developer might accidentally alter the password property in C#. How do we use the ChangeTracker to lock down specific properties from generating SQL updates?"
Architect Answer: "You can manually manipulate the `IsModified` flag on specific properties within the ChangeTracker. Once the object is tracked, you can write: `_context.Entry(user).Property(u => u.PasswordHash).IsModified = false;`. When you call `SaveChanges()`, EF Core scans the object. Even if the password property was egregiously mutated in C# memory, EF Core will explicitly ignore it because you overrode the tracker string. The generated SQL `UPDATE` statement will completely omit the `PasswordHash` column, guaranteeing data integrity at the framework level."