C# tutorials > Language Integrated Query (LINQ) > LINQ to Entities (Entity Framework Core) > What is change tracking in EF Core?
What is change tracking in EF Core?
Change tracking in Entity Framework Core (EF Core) is the mechanism by which EF Core monitors the state of entities retrieved from or added to the database. It automatically detects modifications made to these entities. This allows EF Core to efficiently generate SQL update statements only for the properties that have actually changed, optimizing database interactions and improving application performance. Without change tracking, you would have to manually track and inform EF Core about every single modification you make to your entities, which would be a tedious and error-prone process.
The Basics of Change Tracking
EF Core maintains a snapshot of the original values of the entities when they are queried from the database or added to the context. When SaveChanges()
is called, EF Core compares the current values of the entities with the original values stored in the snapshot. If any differences are detected, EF Core generates the appropriate UPDATE
statements to persist these changes to the database. If no changes are detected, no UPDATE
statements are executed.
Entity States
EF Core tracks entities in various states:
Example: Modifying an Entity and Saving Changes
In this example, we retrieve a Blog
entity from the database. We then modify the Url
property and call SaveChanges()
. EF Core's change tracking automatically detects that the Url
property has been modified and generates the corresponding UPDATE
statement.
using Microsoft.EntityFrameworkCore;
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer("YourConnectionString");
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
public class Example
{
public static void UpdateBlogUrl(int blogId, string newUrl)
{
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(blogId);
if (blog != null)
{
blog.Url = newUrl;
context.SaveChanges(); // EF Core detects the change and generates the UPDATE statement.
}
}
}
}
Accessing the State of an Entity
This example demonstrates how to access the state of an entity using context.Entry(entity)
. You can then access the State
property of the EntityEntry
to determine the current state of the entity. In this case we see the blog moves from Unchanged
to Modified
, then after SaveChanges()
it's back to Unchanged
.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer("YourConnectionString");
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
public class Example
{
public static void CheckBlogState(int blogId)
{
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(blogId);
if (blog != null)
{
EntityEntry entry = context.Entry(blog);
Console.WriteLine($"Blog State: {entry.State}");
blog.Url = "New URL";
Console.WriteLine($"Blog State after modification: {context.Entry(blog).State}");
context.SaveChanges();
Console.WriteLine($"Blog State after SaveChanges: {context.Entry(blog).State}");
}
}
}
}
Real-Life Use Case: Audit Logging
Change tracking is invaluable for implementing audit logging. By overriding the SaveChanges()
method, you can intercept changes to entities before they are persisted to the database. This allows you to create audit logs recording who made the changes, when, and what was changed. This example captures the entity name, the state of the entity (added, modified, deleted), the timestamp of the change, and details about specific property changes.
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System;
using System.Collections.Generic;
using System.Linq;
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<AuditLog> AuditLogs { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> options.UseSqlServer("YourConnectionString");
public override int SaveChanges()
{
var changedEntities = ChangeTracker.Entries()
.Where(e => e.State == EntityState.Added ||
e.State == EntityState.Modified ||
e.State == EntityState.Deleted)
.ToList();
foreach (var entry in changedEntities)
{
var auditLog = new AuditLog
{
EntityName = entry.Entity.GetType().Name,
State = entry.State.ToString(),
Timestamp = DateTime.UtcNow,
Changes = GetChanges(entry)
};
AuditLogs.Add(auditLog);
}
return base.SaveChanges();
}
private string GetChanges(EntityEntry entry)
{
var changes = new List<string>();
foreach (var property in entry.Properties)
{
if (property.IsModified)
{
changes.Add($"{property.Metadata.Name}: OldValue = {property.OriginalValue}, NewValue = {property.CurrentValue}");
}
}
return string.Join(", ", changes);
}
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
public class AuditLog
{
public int AuditLogId { get; set; }
public string EntityName { get; set; }
public string State { get; set; }
public DateTime Timestamp { get; set; }
public string Changes { get; set; }
}
Best Practices
Attach
or Update
before saving changes.AsNoTracking()
for Read-Only Queries: If you are only querying data and not making modifications, use the AsNoTracking()
method to disable change tracking and improve performance.
Interview Tip
When discussing change tracking in interviews, be prepared to explain how EF Core detects changes, the different entity states, and how change tracking can be used for implementing features like audit logging. Also, be ready to discuss the performance implications of change tracking and how to optimize it.
When to use AsNoTracking()
Use AsNoTracking()
when retrieving data for display or reporting purposes where modifications are not required. This can significantly improve performance, especially for large datasets, as EF Core does not need to maintain snapshots of the entities.
Memory Footprint
Change tracking can increase the memory footprint of your application because EF Core needs to store snapshots of the entities being tracked. Be mindful of the number of entities being tracked, especially in long-running contexts. Using AsNoTracking()
can help reduce the memory overhead when tracking is not needed.
Alternatives to EF Core's Change Tracking
While EF Core's change tracking is convenient, in certain scenarios, you might consider alternatives:
Pros of Change Tracking
Cons of Change Tracking
FAQ
-
How do I disable change tracking for a specific query?
Use theAsNoTracking()
method when querying the data. For example:context.Blogs.AsNoTracking().ToList();
-
What happens if I modify an entity that is not being tracked by the context?
EF Core will not detect the changes. You need to explicitly attach the entity to the context usingAttach
orUpdate
before callingSaveChanges()
.Attach
will mark all properties asUnchanged
.Update
will mark all properties asModified
. You can selectively mark individual properties as modified after attaching as well. -
Can I prevent specific properties from being tracked?
No, EF Core does not provide a mechanism to selectively disable change tracking for individual properties. You either track the entire entity or disable tracking for the entire query usingAsNoTracking()
. You could, however, ignore properties from your model entirely so that they are never tracked. Or, in audit logging scenarios, you could choose to not log changes to specific properties.