In EF Core, each entity has a state that determines how it will interact with the database when SaveChanges()
is called.
Entity Framework needs to know what has happened to each entity since it was loaded or created. For that, it uses an internal property called EntityState.
The state of an entity indicates whether EF should insert, update, delete, or simply ignore an entity when saving changes to the database.
State | Value | Description | Generated SQL Example |
---|---|---|---|
Detached | 0 | Not tracked by the DbContext | None |
Unchanged | 1 | Exists in DB and has not changed | None |
Added | 2 | New, will be inserted on save | INSERT INTO... |
Modified | 3 | Existing with pending changes | UPDATE... |
Deleted | 4 | Existing, will be deleted on save | DELETE FROM... |
How the change tracker works
The DbContext
maintains a change tracker that monitors all associated entities. We can inspect it:
var changes = context.ChangeTracker.Entries()
.Where(e => e.State != EntityState.Unchanged)
.ToList();
For example,
var product = new Product { Name = "Laptop" }; // State: Detached
context.Products.Add(product); // State: Added
var productDB = await context.Products.FindAsync(1); // State: Unchanged
productDB.Price = 999; // State: Modified
context.Products.Remove(productDB); // State: Deleted
Automatic state management
Generally, we will let Entity Framework automatically track changes to the entities associated with the DbContext through the Change Tracker.
An entity in the Added state will be inserted into the database when SaveChanges()
is called. Entity Framework will generate an INSERT SQL statement for this entity.
// Example: Create a new student
var newStudent = new Student { Name = "Ana López", Age = 20 };
context.Students.Add(newStudent);
// Now newStudent is in the Added state
context.SaveChanges(); // Generates an INSERT
Entities in the Unchanged state already exist in the database and have not been modified since they were retrieved. They do not generate any SQL operation in SaveChanges()
.
// Querying a student
var student = context.Students.Find(1);
// student is in the Unchanged state after being retrieved
An entity in the Modified state will be updated in the database with an UPDATE SQL statement when SaveChanges()
is called.
// Modifying an existing student
var student = context.Students.Find(1);
student.Name = "Ana María López";
// Entity Framework automatically detects the change
context.SaveChanges(); // Generates an UPDATE
An entity in the Deleted state will be removed from the database using a DELETE SQL statement when SaveChanges()
is called.
// Deleting a student
var student = context.Students.Find(1);
context.Students.Remove(student);
// student is now in the Deleted state
context.SaveChanges(); // Generates a DELETE
An entity in the Detached state is not being tracked by the Entity Framework context. This means that any changes to this entity will not be detected or automatically persisted.
// Creating a detached entity
var detachedStudent = new Student { Id = 1, Name = "Carlos" };
// detachedStudent is in the Detached state
Manual state manipulation
We can also manually change the state. This is not common but sometimes necessary for advanced operations.
context.Entry(product).State = EntityState.Modified;