ChangeTracker in Entity Framework Core

The DbContext in Entity Framework Core includes the ChangeTracker class in Microsoft.EntityFrameworkCore.ChangeTracking namespace which is responsible of tracking the state of each entity retrieved using the same DbContext instance. It is not intended to use it directly in your application code because it may change in future versions. However, you can use some methods for tracking purpose.

The ChangeTracker class in Entity Framework Core starts tracking of all the entities as soon as it is retrieved using DbContext, until they go out of its scope. EF keeps track of all the changes applied to all the entities and their properties, so that it can build and execute appropriate DML statements to the underlying data source.

An entity at any point of time has one of the following states which are represented by the enum Microsoft.EntityFrameworkCore.EntityState in EF Core.

  • Added
  • Modified
  • Deleted
  • Unchanged
  • Detached

Let's see how the EntityState is changed automatically based on the action performed on the entity.

Unchanged State

First, all the entities retrieved using direct SQL query or LINQ-to-Entities queries will have the Unchanged state.

public static void Main()
{
    using (var context = new SchoolContext())
    {
        // retrieve entity 
        var student = context.Students.First();
        DisplayStates(context.ChangeTracker.Entries());
    }
}

private static void DisplayStates(IEnumerable<EntityEntry> entries)
{
    foreach (var entry in entries)
    {
        Console.WriteLine($"Entity: {entry.Entity.GetType().Name},
                             State: {entry.State.ToString()} ");
    }
}
Output:
Entity: Student, State: Unchanged

Added State

All the new entities without key property value, added in the DbContext using the Add() or Update() method will be marked as Added.

using (var context = new SchoolContext())
{              
    context.Add(new Student() { FirstName = "Bill", LastName = "Gates" });
    
    DisplayStates(context.ChangeTracker.Entries());
}
Output:
Entity: Student, State: Added

Modified State

If the value of any property of an entity is changed in the scope of the DbContext, then it will be marked as Modified state.

using (var context = new SchoolContext())
{
    var student = context.Students.First();
    student.LastName = "LastName changed";
              
    DisplayStates(context.ChangeTracker.Entries());
}
Output:
Entity: Student, State: Modified

Deleted State

If any entity is removed from the DbContext using the DbContext.Remove or DbSet.Remove method, then it will be marked as Deleted.

using (var context = new SchoolContext())
{
    var student = context.Students.First();
    context.Students.Remove(student);
    
    DisplayStates(context.ChangeTracker.Entries());
}
Output:
Entity: Student, State: Modified

Detached State

All the entities which were created or retrieved out of the scope of the current DbContext instance, will have the Detached state. They are also called disconnected entities and are not being tracked by an existing DbContext instance.

var disconnectedEntity = new Student() { StudentId = 1, Name = "Bill" };

using (var context = new SchoolContext())
{              
    Console.Write(context.Entry(disconnectedEntity).State);
}
Output:
Detached

In the above example, disconnectedEntity is created out of the scope of DbContext instance (context). So, it is in the Detached state for the context.