Entity Framework Core: To Track or Not To Track?

EF Core: When to Use AsNoTracking for Best Performance

Ever wonder why your EF Core queries seem snappy in dev but sluggish in production? The answer might lie in a subtle detail: change tracking. Understanding how EF Core tracks entities can be the difference between a lightning-fast API and a crawling one.

By default, EF Core tracks every entity it retrieves. This allows it to detect changes for updates, but at a performance cost. For read-only queries, this tracking is unnecessary overhead.

Enter AsNoTracking(). Adding this to your LINQ queries tells EF Core, “Hey, I’m just browsing.” It skips tracking, reducing memory usage and boosting speed:

using var context = new AppDbContext();
var products = await context.Products
    .AsNoTracking()
    .Where(p => p.IsActive)
    .ToListAsync();

But that’s not the end of the story. The DbContext itself is scoped—usually per web request in ASP.NET Core. Reusing a context across requests or for too long can create a bloated change tracker, leading to memory leaks and degraded performance. Always scope your DbContext properly, and avoid long-lived contexts unless you have a solid reason.

There’s also a middle ground: AsNoTrackingWithIdentityResolution(). It avoids duplicate entities but still skips change tracking. Useful for projections or deserialization scenarios.

Pitfalls? Yes. Avoid AsNoTracking() if you plan to update the entity later—it won’t be tracked, so SaveChanges() won’t know what’s changed.

So, to track or not to track? For read-heavy operations, go no-tracking. For updates, let EF track. The real win is knowing when each fits.

Want to squeeze more performance from EF Core? Check out our post on query splitting and compiled queries next!

Leave a Reply

Your email address will not be published. Required fields are marked *