Introduction

In this article, we will learn about EF Core Tips to Improve Read Performance with AsNoTracking().

Before we start, please take a look at my last article on Entity Framework.

Now, let's get started.

When working with Entity Framework Core (EF Core), one of the easiest ways to boost performance for read-heavy operations is by using AsNoTracking(). This small change can make a big difference in applications like APIs, dashboards, and reporting systems.

🔍 The Problem: Default Tracking Adds Overhead

By default, EF Core tracks every entity it retrieves. This allows it to:

However, this tracking comes at a cost:

👉 If you're only reading data, this tracking is unnecessary.

✅ The Solution: AsNoTracking()

AsNoTracking() tells EF Core:

“Fetch the data, but don’t track it in the DbContext.”

🧪 Example Scenario

Let's say you're building a Product API that returns a list of active products.

🔴 Without AsNoTracking()

public async Task<List<Product>> GetActiveProducts()
{
    return await _context.Products
        .Where(p => p.IsActive)
        .ToListAsync();
}

Issues

🟢 With AsNoTracking()

public async Task<List<Product>> GetActiveProducts()
{
    return await _context.Products
        .AsNoTracking()
        .Where(p => p.IsActive)
        .ToListAsync();
}

✅ Benefits

⚡ Real-World Use Case

Imagine a dashboard showing:

All of these are read-only views.

public async Task<List<OrderDto>> GetRecentOrders()
{
    return await _context.Orders
        .AsNoTracking()
        .OrderByDescending(o => o.CreatedDate)
        .Select(o => new OrderDto
        {
            Id = o.Id,
            CustomerName = o.Customer.Name,
            TotalAmount = o.TotalAmount
        })
        .ToListAsync();
}

👉 This avoids tracking thousands of rows unnecessarily and keeps your API responsive.

❗ Important Caveat

Entities retrieved using AsNoTracking() are not tracked, so changes won’t be saved automatically.

❌ This Will NOT Work

var product = await _context.Products
    .AsNoTracking()
    .FirstAsync(p => p.Id == 1);

product.Price = 100;
await _context.SaveChangesAsync(); // No update!

✅ Correct Approach

_context.Products.Update(product);
await _context.SaveChangesAsync();

—or fetch the entity without AsNoTracking() if you intend to update it.

🆚 When Should You Use It?

✅ Use AsNoTracking() When

❌ Avoid When

💡 Pro Tip: Make No-Tracking the Default

If most of your queries are read-only:

optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);

You can still override per query:

context.Products.AsTracking().FirstOrDefaultAsync();

🧠 Advanced: Identity Resolution

AsNoTrackingWithIdentityResolution()

Useful For

✅ Summary

🚀 Final Thought

If your application is read-heavy (which most modern apps are), start using AsNoTracking() consistently—it’s one of the simplest ways to scale EF Core efficiently.

Conclusion

In this article, I have tried to cover EF Core Tips to Improve Read Performance with AsNoTracking().