100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C#

Change Tracking Explained

How EF Core's ChangeTracker snapshots entity state and computes the minimal set of INSERT, UPDATE, and DELETE statements needed at SaveChanges time.

Performance & TransactionsIntermediate9 min readJul 10, 2026
Analogies

What the Change Tracker Actually Does

Every time you pull entities out of a DbContext using a tracking query, EF Core doesn't just hand you plain objects — it registers each one with an internal component called the ChangeTracker. That tracker keeps a snapshot of the values each entity had when it was loaded, so that when you call SaveChanges, EF can compare current values against the snapshot and generate only the UPDATE statements that are actually needed.

🏏

Cricket analogy: Like a third umpire reviewing a run-out, the ChangeTracker keeps a frozen frame of exactly where every entity stood at load time so it can compare that frame against the current position before deciding what happened.

Entity States

Every tracked entity is assigned one of five EntityState values: Detached, Unchanged, Added, Modified, or Deleted. A freshly queried entity starts as Unchanged; calling context.Add() marks it Added; mutating a scalar property flips it to Modified; and context.Remove() marks it Deleted. SaveChanges walks the tracker, translates each non-Unchanged entity into the corresponding INSERT, UPDATE, or DELETE, and then resets everything back to Unchanged (or Detached for removed rows) once the transaction commits.

🏏

Cricket analogy: It's like the five statuses a batter can have on the scorecard — not out, retired, run out, stumped, or caught — each one dictating exactly what entry the scorer writes next to their name.

Detecting Changes: Snapshot Tracking vs Change-Tracking Proxies

By default EF Core uses snapshot change tracking: it clones the original property values into a separate ChangeTracker.Entries() shadow store and performs a value-by-value comparison (DetectChanges) whenever SaveChanges, Entry(), or ChangeTracker.DetectChanges() is invoked. This is convenient because your POCOs stay plain, but DetectChanges has to walk every tracked entity's every property, which can get expensive with thousands of tracked rows. As an alternative, EF Core supports change-tracking proxies (via UseChangeTrackingProxies) that generate dynamic subclasses implementing INotifyPropertyChanged, so changes are flagged immediately on assignment instead of being discovered by a later full scan.

🏏

Cricket analogy: It's the difference between Hawk-Eye reconstructing a ball's full trajectory after the fact versus a stump-mic sensor that pings the instant the bail is dislodged — one recomputes everything, the other reacts immediately.

csharp
// Inspecting and manipulating entity state directly
using var context = new BloggingContext();

var blog = context.Blogs.First(b => b.BlogId == 1);
Console.WriteLine(context.Entry(blog).State); // Unchanged

blog.Url = "https://updated-example.com";
Console.WriteLine(context.Entry(blog).State); // Modified

// Force EF to recompute change state without calling SaveChanges
context.ChangeTracker.DetectChanges();

foreach (var entry in context.ChangeTracker.Entries())
{
    Console.WriteLine($"{entry.Entity.GetType().Name} is {entry.State}");
}

context.SaveChanges(); // Entries reset to Unchanged after commit

ChangeTracker.Entries() lets you inspect every tracked entity and its state before calling SaveChanges — useful for auditing or building generic logging middleware around SaveChanges.

  • The ChangeTracker stores an original-value snapshot for every tracked entity so SaveChanges can compute a minimal diff.
  • Every tracked entity has one of five states: Detached, Unchanged, Added, Modified, Deleted.
  • SaveChanges converts non-Unchanged entities into INSERT, UPDATE, or DELETE statements, then resets state to Unchanged.
  • Default snapshot tracking calls DetectChanges to compare current vs. original values, which scales with the number of tracked entities and properties.
  • Change-tracking proxies (UseChangeTrackingProxies) avoid full DetectChanges scans by reacting to property assignment immediately via INotifyPropertyChanged.
  • ChangeTracker.Entries() exposes every tracked entity's EntityState for debugging, auditing, or logging.

Practice what you learned

Was this page helpful?

Topics covered

#EntityFrameworkCoreStudyNotes#MicrosoftTechnologies#ChangeTrackingExplained#Change#Tracking#Explained#Tracker#StudyNotes#SkillVeris#ExamPrep