In this lesson, you will learn how to choose between tracked and untracked queries to optimize your application’s performance and memory footprint.
Understanding Entity Tracking
When you query a database using Entity Framework Core, it uses a mechanism called "Change Tracking" by default. This means that every object returned by your query is added to the DbContext’s internal tracker. The tracker monitors these objects to see if their properties change. If you modify an object and call SaveChanges(), EF Core automatically generates the UPDATE SQL to persist your changes.
While this makes updating data effortless, it carries a performance cost. The tracker consumes memory and CPU cycles to keep a snapshot of every object in its "original" state for comparison.
When to Use Untracked Queries
If you are simply displaying a list of products or generating a report, you do not need the DbContext to track those entities. In these cases, you should use AsNoTracking().
By using AsNoTracking(), you tell EF Core to stream the data directly to you without performing the extra work of snapshotting the objects. This reduces memory usage and speeds up query execution significantly.
Code Comparison
Here is how you swap between the two modes:
// Tracked: Use this if you plan to modify the items and save them later.
var productsToUpdate = context.Products.Where(p => p.Price < 10).ToList();
// Untracked: Use this for read-only scenarios (e.g., UI displays, PDFs, reports).
var readOnlyProducts = context.Products
.AsNoTracking()
.Where(p => p.Price < 10)
.ToList();
The Balancing Act
Choosing the strategy comes down to intent:
- Tracked: Essential for "Unit of Work" patterns where you retrieve data, change properties, and save. The overhead is the price you pay for automatic change management.
- Untracked: Ideal for high-performance read scenarios. Since these objects are disconnected from the
DbContext, you cannot simply change a property and callSaveChanges()to update the database; you would have to manually re-attach or perform an explicit update operation.
Key Takeaway: Use AsNoTracking() by default for all read-only queries to improve application performance and memory efficiency, reserving tracked queries only for scenarios where you explicitly intend to modify and persist entity states.