entity-framework-tracking-y-no-tracking

Tracking vs No-Tracking in Entity Framework

  • 4 min

The Tracking is the mechanism by which Entity Framework tracks changes to the entities retrieved from the database.

Tracking is one of the best features of Entity Framework. However, it can also be one of the most costly in terms of performance.

For this reason, sometimes it will be beneficial to disable the system using NoTracking, to optimize our applications.

This is especially important when working with large volumes of data or queries that do not require modifications to the entities.

What is Tracking in Entity Framework?

Tracking is the system that allows Entity Framework to determine changes when we make modifications to these entities.

With this, it can generate the necessary SQL statements to update the database when we call SaveChanges().

// Query with Tracking (default)
var product = context.Products.FirstOrDefault(p => p.Id == 1);

// We modify the entity
product.Price = 99.99m;

// We save the changes to the database
context.SaveChanges();

In this example,

  • Entity Framework is tracking the product entity.
  • When we modify the price and call SaveChanges(),
  • EF detects the change and generates an SQL UPDATE statement to update the record in the database.

But, logically, this tracking of changes comes with overhead that we may not always need. Therefore, we can disable it if we do not require it.

What is No-Tracking in Entity Framework?

The No-Tracking is a technique that disables tracking of the retrieved entities. This means that Entity Framework will not track changes to these entities.

To disable tracking, we use the AsNoTracking() method.

using (var context = new ApplicationDbContext())
{
    // Query with No-Tracking
    var products = context.Products
                           .AsNoTracking()
                           .ToList();

    // Changes to these entities will not be tracked
    foreach (var product in products)
    {
        product.Price = 99.99m; // This change will not be reflected in the database
    }
}

In this case,

  • The retrieved entities are not being tracked by Entity Framework.
  • Any modifications we make to these entities will not be reflected in the database when calling SaveChanges().

AsNoTracking with projections

Sometimes, we do not need to retrieve complete entities, just certain fields. In these cases, we can also use AsNoTracking with projections to further improve performance.

using (var context = new ApplicationDbContext())
{
    var products = context.Products
                           .AsNoTracking()
                           .Select(p => new { p.Name, p.Price })
                           .ToList();
}

In this example, we only retrieve the Name and Price fields of the products, which reduces the amount of data transferred and improves performance.

Updating an entity loaded with AsNoTracking

Even if we have loaded an entity with AsNoTracking, we can still update it. However, we will have to manage the state manually (we do not have tracking).

To do this, we need to attach it manually and set the state to Modified.

var entityWithoutTracking = context.Entities
    .AsNoTracking()
    .FirstOrDefault(e => e.Id == id);

// Make changes to the entity
entityWithoutTracking.Name = "New Name";

// Attach the entity to the context (still not tracking)
context.Attach(entityWithoutTracking);

// Attach and mark as modified
context.Entry(entityWithoutTracking).State = EntityState.Modified;

// Save the changes
context.SaveChanges();
  • You must ensure that the key fields are correctly assigned, as EF will use that to know which record to update (like the Id)

  • EF cannot detect which properties changed when you use State = Modified, so it will update all columns of that row, not just the modified ones.

  • If your entity has navigation properties, be careful with relationships you do not want to touch; they could be updated or attempted to be inserted if not properly controlled.

There are more ways to change the state of a No-Tracking entity. For example, the Update command is basically equivalent to Attach and changing the state.

context.Update(product)

On the other hand, if we want to mark only the state of a property, we could do it like this

context.Entry(product).Property(p => p.Price).IsModified = true;

When to use No-Tracking

Entities loaded with No-Tracking reduce memory overhead and improve speed in database access (they improve it a lot!)

However, we lose tracking 🙄. So if we make changes to the entities, we won’t be able to save them (easily) with SaveChanges.

Therefore, it is useful in

  • Read-only queries: If you only need to retrieve data to display or process it without modifying it
  • Large volumes of data: When working with large datasets that require significant performance optimization