Boost EF Core Read‑Only Query Performance with AsNoTracking
Learn how to use EF Core’s AsNoTracking to skip change‑tracking, cut query time, and avoid unnecessary memory use. Includes a step‑by‑step guide, a concrete example, and verification tips.
23 Sept 2025, 05:58 UTC

Desired Outcome
Return large sets of data from EF Core without the overhead of change tracking. The goal is to reduce CPU, memory usage, and round‑trip time for read‑only scenarios such as reporting or API responses.
Prerequisites
- EF Core 2.0 or newer (all supported versions).
- Basic knowledge of DbContext, DbSet, and LINQ queries.
- Access to the application’s logging configuration.
- An existing database with a populated table for testing.
Step‑by‑Step Procedure
- Enable EF Core Logging (Optional but Recommended)
Configure a
LoggerFactoryto output SQL and context activity to the console. This helps verify that change‑tracking statements are omitted.public class MyDbContext : DbContext { public static readonly ILoggerFactory LoggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => { builder.AddConsole(); }); protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer("YourConnectionString") .UseLoggerFactory(LoggerFactory); } } - Apply AsNoTracking to the Query
Use the
AsNoTracking()extension on anyIQueryable<T>coming from aDbSetor LINQ expression.var orders = await dbContext.Orders .Where(o => o.Status == OrderStatus.Pending) .AsNoTracking() // Key line .ToListAsync(); - Verify No Change‑Tracking Entries
After executing the query, check that the context’s change tracker is empty.
Console.WriteLine("Entries tracked: " + dbContext.ChangeTracker.Entries().Count()); // Expected output: 0 - Measure Performance (Optional)
Run the same query twice: once with
AsNoTrackingand once without. Compare execution times and CPU usage.var sw = Stopwatch.StartNew(); var list = await dbContext.Orders.AsNoTracking().ToListAsync(); sw.Stop(); Console.WriteLine($"AsNoTracking took {sw.ElapsedMilliseconds} ms"); - Handle Navigation Properties
Because lazy loading is disabled for no‑tracked queries, eagerly load related data if needed.
var ordersWithItems = await dbContext.Orders .Where(o => o.Status == OrderStatus.Pending) .Include(o => o.Items) // eager load .AsNoTracking() .ToListAsync(); - Recovering from Unintended Tracking
If you accidentally modify an entity returned with
AsNoTracking, EF Core will ignore the changes. To persist them, attach the entity and set its state.var order = orders.First(); order.Status = OrderStatus.Completed; // change made // Attach and mark as modified var entry = dbContext.Entry(order); entry.State = EntityState.Modified; await dbContext.SaveChangesAsync();
Expected Checks
- The SQL generated by EF Core should not include additional SELECTs for entity snapshots.
dbContext.ChangeTracker.Entries().Count()returns0after a no‑tracking query.- When attempting to
SaveChangeson a modified no‑tracked entity without re‑attaching, noUPDATEstatement is sent.
Practical Verification Example
Set up a unit test using xUnit and an in‑memory provider to confirm the behavior.
[Fact]
public async Task NoTracking_DoesNotTrackEntities()
{
var options = new DbContextOptionsBuilder()
.UseInMemoryDatabase("TestDb")
.Options;
using (var context = new MyDbContext(options))
{
context.Orders.Add(new Order { Id = 1, Status = OrderStatus.Pending });
await context.SaveChangesAsync();
}
using (var context = new MyDbContext(options))
{
var orders = await context.Orders.AsNoTracking().ToListAsync();
Assert.Empty(context.ChangeTracker.Entries());
}
}
Limitations and Caveats
- Entities are not tracked; any modifications will be ignored unless you re‑attach.
- Lazy loading and automatic navigation fix‑up are disabled. Use
Includeor explicit loading. - Complex queries that rely on change‑tracking for concurrency checks or shadow properties will fail.
- EF Core 2.0+ only; older projects must upgrade to use this feature.
Conclusion
Applying AsNoTracking is a lightweight, declarative way to boost read‑only query performance in EF Core. Use it whenever you only need data for display or reporting, and remember to handle navigation properties explicitly and re‑attach entities if updates are required.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.