Table of contents
What is the role of entity states in EF Core change tracking?
Short Answer
Every object tracked by a DbContext has an entity state. EF Core uses this state to decide what SQL operation — if any — to generate when SaveChanges() is called.
The Five Entity States
| State | What it means | SQL generated |
|---|---|---|
|
|
Entity is new and not yet in the database |
|
|
|
Entity exists in the DB and at least one property changed |
|
|
|
Entity exists in the DB and is marked for removal |
|
|
|
Entity exists in the DB and nothing has changed | (none) |
|
|
Entity is not being tracked by the DbContext at all | (none) |
State Lifecycle
stateDiagram-v2
[*] --> Detached : Entity created (new object)
Detached --> Added : DbSet.Add() / context.Add()
Detached --> Unchanged : DbSet.Attach() or queried from DB
Unchanged --> Modified : Property value changed
Unchanged --> Deleted : DbSet.Remove()
Added --> Detached : SaveChanges() completes (new PK assigned)
Modified --> Unchanged : SaveChanges() completes
Deleted --> Detached : SaveChanges() completes
Added --> Detached : Entry.State = Detached
How to Read and Set Entity State
You can inspect or manually override the state through the ChangeTracker:
var entry = context.Entry(myEntity);
// Read the current state
EntityState currentState = entry.State;
// Manually set the state
entry.State = EntityState.Modified;
Practical Example
using var context = new AppDbContext();
// State: Detached (not tracked yet)
var product = new Product { Name = "Keyboard" };
context.Products.Add(product);
// State: Added
await context.SaveChangesAsync();
// State: Unchanged (INSERT was executed, EF now tracks it)
product.Name = "Mechanical Keyboard";
// State: Modified (EF detected the property change)
await context.SaveChangesAsync();
// State: Unchanged (UPDATE was executed)
context.Products.Remove(product);
// State: Deleted
await context.SaveChangesAsync();
// State: Detached (DELETE was executed, entity leaves tracking)
Key Takeaways
- EF Core's
DbContextacts as a Unit of Work — it accumulates state changes and flushes them in oneSaveChanges()call. - State transitions happen automatically when you query data, modify properties, or call
Add()/Remove(). - The
Detachedstate is the default for any object theDbContextdoesn't know about — this is common in web APIs where entities are deserialized from JSON. - You can view all tracked entities and their states via
context.ChangeTracker.Entries().
Have you heard about Fluent API?
Yes. In EF Core, Fluent API is the code-based way to configure how your entity model maps to the database. You usually write that configuration inside OnModelCreating, where you define keys, relationships, column types, required fields, indexes, and delete behavior.
It does not replace change tracking or entity states. Instead, it complements them. Entity states describe what EF Core should do with an object at runtime, while Fluent API defines how that object should be stored and constrained in the database schema.
For example, you can use Fluent API to make a property required, limit its length, and configure a one-to-many relationship:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>(entity =>
{
entity.HasKey(order => order.Id);
entity.Property(order => order.Number)
.IsRequired()
.HasMaxLength(50);
entity.HasOne(order => order.Customer)
.WithMany(customer => customer.Orders)
.HasForeignKey(order => order.CustomerId)
.OnDelete(DeleteBehavior.Restrict);
});
}
This matters in interviews because it shows you understand the difference between model configuration and runtime tracking. If an interviewer asks about entity states, Fluent API is relevant as supporting context: it shapes the model EF Core tracks, but it does not decide whether an entity is Added, Modified, or Deleted.
What is the difference between tracking and no-tracking queries in EF Core?
Alternative Question Titles
Answer
In EF Core, a tracking query stores returned entities in the DbContext change tracker. A no-tracking query skips that step and returns entities as plain objects not managed by the context.
Tracking matters because EF Core can detect changes automatically and generate UPDATE/DELETE statements on SaveChanges(). No-tracking matters because it usually uses less memory and CPU for read-only operations.
Use tracking for workflows where data is loaded and then modified in the same context. Use no-tracking for read-only endpoints like list pages, reporting, dashboards, or API responses.
// Read-only endpoint: prefer no-tracking
var products = await context.Products
.AsNoTracking()
.Where(p => p.IsActive)
.ToListAsync();
// Update workflow: keep tracking on
var product = await context.Products
.FirstAsync(p => p.Id == id);
product.Name = "Updated name";
await context.SaveChangesAsync();
