When EF Core downloads data from SQL Server, it doesn't just hand you the C# objects. By default, it takes a "snapshot" of those objects and stores them inside the DbContext ChangeTracker memory. If you modify the object later, EF Core compares the object against its snapshot to figure out what SQL UPDATE statement to write. This snapshotting process consumes massive amounts of CPU and RAM.
If you query 10,000 products just to display them on a "Catalog" webpage, you are completely wasting memory. You have absolutely no intention of Updating or Deleting those products; they are read-only. Yet EF Core still painstakingly builds 10,000 snapshots in the background.
To eliminate this overhead, you append the .AsNoTracking() method to your LINQ query. This tells EF Core: "Just give me the raw data. Do not track it. Do not snapshot it."
// ❌ BAD: Wastes RAM by tracking objects we only meant to read
var products = await _context.Products.ToListAsync();
// ✅ GOOD: Bypasses the Change Tracker completely! Up to 40% faster on large queries.
var optimizedProducts = await _context.Products.AsNoTracking().ToListAsync();
If you are building a pure "Reporting API" where almost every single query is read-only, you don't want to type .AsNoTracking() 500 times. You can change the default behavior of the entire DbContext.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer("...");
// Every query executed by this context will default to No-Tracking!
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
});
.AsTracking() to that specific LINQ query.
Q: "We queried a User using `.AsNoTracking()`. Later in the same method, we decided we DO want to update the user's name. So we changed the name on the untracked C# object and called `await _context.SaveChangesAsync()`. Why did the database not update?"
Architect Answer: "Because EF Core has absolutely no idea that the object exists. When you use `AsNoTracking()`, the object is completely orphaned from the DbContext the moment it is downloaded. Calling `SaveChanges()` literally does nothing because the Change Tracker's internal list of 'Modified Entities' is completely empty. If you want to update an untracked entity, you must explicitly re-attach it to the DbContext using `_context.Update(user);`. This forces EF Core to start tracking the object again and marks its state as Modified, ensuring that `SaveChanges` generates the SQL `UPDATE` statement."