Tracking vs No-Tracking – Impact on Performance and Memory
EF Core’s default change tracking is a double-edged sword: it enables automatic updates
but consumes memory and CPU. Choosing AsNoTracking()
wisely can cut GC pressure by
90 % on read-heavy endpoints without losing correctness.
Real-Life Analogy: Security Cameras vs Click Counters
A museum can film every visitor with CCTV (tracking) or just click a counter at the door (no-tracking). Cameras capture rich behaviour but store gigabytes; clickers are cheap but blind to theft. In code: track entities you plan to update, click-count the rest.
How Tracking Works
- Identity Map – dictionary keyed by primary-key ➜ keeps single CLR instance per row.
- Snapshot Storage – original values captured after materialisation or
AcceptAllChanges()
. - DetectChanges() – walks all tracked entities comparing current vs snapshot.
- Change Graph – generates
Insert/Update/Delete
commands.
Memory Footprint Benchmark
// 100 k Order rows, .NET 8, SQL Server 2022, Release build
Query Gen0 KB Time (ms)
---------------------------------------------------------------
Tracked ToListAsync() 480 000 1900
AsNoTracking().ToListAsync() 60 000 1400
AsNoTrackingWithIdentityResolution() 110 000 1500
Decision Matrix
Scenario | Tracking? | Notes |
---|---|---|
Read-only API list | No | Return DTOs, not entities. |
Edit Form (load + update) | Yes | Attach automatically, simplifies PATCH. |
Background export | No | Stream to CSV. |
Graph update with Attach() | Yes | Change tracker resolves reference graph. |
Advanced: Clearing & Splitting Contexts
context.ChangeTracker.Clear()
– drop all tracked entities mid-request.- CQRS Pattern – query stack uses no-tracking, command stack uses tracking.
- Scoped DbContext per operation – cheapest way to isolate.
Identity Resolution Without Tracking
AsNoTrackingWithIdentityResolution()
gives deduplicated references (good for JSON loops)
without snapshots—memory halfway between the two modes.
Final Thoughts
Tracking is like a high-def CCTV system: switch it on when you need evidence, turn it off when you’re just counting visitors. Declare intent explicitly—your GC and latency graphs will smile back.