Here you will learn how DbContext tracks the changes of entities and modify their state. This will allow it to perform insert, update or delete operation based on the states.
The DbContext in Entity Framework Core includes the ChangeTracker property of type ChangeTracker
class which is responsible of tracking the state of each entity retrieved using the DbContext
instance.
Note that it is not intended to use it directly in your application code. It is just to understand how EF track changes of entities.
The ChangeTracker
starts tracking of all the entities as soon as it is retrieved using the object of the DbContext
class, 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.
Let's see how the EntityState
is changed automatically based on the action performed on the entity.
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.FirstOrDefault(); DisplayStates(context.ChangeTracker.Entries()); } } static void DisplayStates(IEnumerable<EntityEntry> entries) { foreach (var entry in entries) { Console.WriteLine($"Entity: {entry.Entity.GetType().Name}, State: {entry.State.ToString()} "); } }
In the above example, we fetch the student entity using context.Students.FirstOrDefault()
method. As soon as we retrieve it, the context class starts tracking it.
We pass the context.ChangeTracker.Entries()
to the DesplayStates()
method.
The context.ChangeTracker.Entries()
returns a collection of EntityEntry for each entity being tracked by the context.
The DisplayStates()
method iterates through each entity entry in the provided EntityEntry
collection using foreach loop
.
It prints the name of the entity type (e.g., Student, Grade, etc.) obtained using entry.Entity.GetType().Name
and the state of the entity obtained using entry.State.ToString()
.
We don't perform any operation on the student
object, so the status will be "Unchanged".
It means when calling the context.SaveChanges()
method, nothing will happen. No DB query will be executed as no entity has been changed in the scope of the context
object.
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.Students.Add(new Student() { FirstName = "Bill", LastName = "Gates" }); DisplayStates(context.ChangeTracker.Entries()); }
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.FirstOrDefault(); student.LastName = "Friss"; DisplayStates(context.ChangeTracker.Entries()); }
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.FirstOrDefault(); context.Students.Remove(student); DisplayStates(context.ChangeTracker.Entries()); }
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); }
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.
Learn how to save changes to the database in the next statement.