Skip to content

EF Core No-Tracking — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about EF Core No. We cover key concepts, practical examples, and best practices.

Every query loads entities into the change tracker by default. For read-only data — dropdown options, report data, public listings — tracking wastes memory and CPU. AsNoTracking() tells EF Core to skip the change tracker entirely.

Wrong

public List<Product> GetProducts()
{
    return await db.Products.ToListAsync(); // Default tracking
}

Output: All products are tracked. If there are 10,000 products, the change tracker holds 10,000 snapshots. Memory grows, and SaveChanges runs change detection on all of them (even though none changed).

public List<Product> GetProducts()
{
    return await db.Products.AsNoTracking().ToListAsync();
}

Output: Same data. No tracking. Zero change tracker overhead. Memory is freed after the list is materialized.

Global configuration:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(connectionString)
        .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
}

// Override per-query when you need tracking:
var order = await db.Orders.AsTracking().FirstAsync(o => o.Id == id);
order.Status = "Shipped";
await db.SaveChangesAsync();

Prevention

  • Use AsNoTracking() for GET endpoints, reports, and public data.
  • Use AsNoTrackingWithIdentityResolution() when you need deduplication without tracking.
  • Set NoTracking as the global default for read-heavy contexts.
  • Use AsTracking() for the few queries that need to update entities.
  • Combine AsNoTracking with AsSplitQuery for complex read-only queries.
  • Do not use AsNoTracking when you need identity resolution across multiple Include calls.

Common Mistakes with core no tracking

  1. Misunderstanding that String is [Char] with poor performance for large text operations
  2. Using foldl instead of foldl' causing stack overflow on large lists
  3. Forgetting deriving (Show, Eq) on custom data types needed for debugging

These mistakes appear frequently in real-world EF code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

Can I update an entity loaded with AsNoTracking?

Yes. Call db.Attach(entity) to start tracking it, then set the state to Modified or make changes and call db.Update(entity). Without this, SaveChanges ignores no-tracking entities.

Does AsNoTracking affect lazy loading?

Yes. Lazy loading requires tracking — the proxy needs the change tracker to detect navigation property access. AsNoTracking disables lazy loading for that query. Use explicit Include for navigation properties with no-tracking.

Is there any case where tracking is faster?

For small result sets (under 100 entities), the tracking overhead is negligible. For scenarios where most entities are modified, tracking is necessary. For read-only or mostly-read scenarios, no-tracking is always faster or equal.

No-tracking queries are standard in DodaTech's public-facing APIs. For more EF Core, visit DodaTech.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro