AsNoTracking in EF Core
What is AsNoTracking?
AsNoTracking disables change tracking for a query, improving performance for read-only scenarios where you don’t need to update the retrieved entities.
Basic Usage
// With tracking (default)
var products = await context.Products.ToListAsync();
// Without tracking
var products = await context.Products
.AsNoTracking()
.ToListAsync();Performance Benefits
// Benchmark comparison
// With tracking: ~100ms, 50MB memory
var tracked = await context.Products.ToListAsync();
// Without tracking: ~60ms, 30MB memory
var untracked = await context.Products
.AsNoTracking()
.ToListAsync();When to Use AsNoTracking
Read-Only Queries
// API endpoints returning data
public async Task<IActionResult> GetProducts()
{
var products = await context.Products
.AsNoTracking()
.ToListAsync();
return Ok(products);
}Reporting Queries
var report = await context.Orders
.AsNoTracking()
.Include(o => o.OrderItems)
.Where(o => o.OrderDate >= startDate)
.Select(o => new OrderReport
{
OrderId = o.Id,
TotalAmount = o.TotalAmount,
ItemCount = o.OrderItems.Count
})
.ToListAsync();Large Dataset Queries
var allProducts = await context.Products
.AsNoTracking()
.ToListAsync(); // Better performance for large datasetsAsNoTrackingWithIdentityResolution
// Resolves duplicate entities in result set
var orders = await context.Orders
.AsNoTrackingWithIdentityResolution()
.Include(o => o.Customer)
.Include(o => o.OrderItems)
.ThenInclude(oi => oi.Product)
.ToListAsync();Global Configuration
// Set default tracking behavior
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking));
// Override for specific query
var products = await context.Products
.AsTracking()
.ToListAsync();Limitations
// Cannot update untracked entities
var product = await context.Products
.AsNoTracking()
.FirstAsync(p => p.Id == 1);
product.Price = 99.99m;
await context.SaveChangesAsync(); // No update - entity not tracked
// Need to attach and set state
context.Products.Attach(product);
context.Entry(product).State = EntityState.Modified;
await context.SaveChangesAsync(); // Now updatesBest Practices
// 1. Use for read-only scenarios
var products = await context.Products
.AsNoTracking()
.ToListAsync();
// 2. Use with projections
var productDtos = await context.Products
.AsNoTracking()
.Select(p => new ProductDto
{
Id = p.Id,
Name = p.Name,
Price = p.Price
})
.ToListAsync();
// 3. Don't use when you need to update
var product = await context.Products
.FirstAsync(p => p.Id == 1); // Keep tracking for updates
product.Price = 99.99m;
await context.SaveChangesAsync();Performance Comparison
| Scenario | With Tracking | AsNoTracking | Improvement |
|---|---|---|---|
| 1000 entities | 100ms | 60ms | 40% faster |
| Memory usage | 50MB | 30MB | 40% less |
| 10000 entities | 1000ms | 500ms | 50% faster |
Summary
AsNoTracking improves performance by disabling change tracking for read-only queries. Use it for API responses, reports, and large datasets. Don’t use it when you need to update entities.
Test Your Knowledge
Take a quick quiz to test your understanding of this topic.