A "Disconnected Entity" scenario occurs constantly in Web APIs. A front-end React application downloads a Product JSON object via a GET request. The user edits the price in the browser. 10 minutes later, the React app sends the modified JSON back via a PUT request. The EF Core DbContext that originally served the GET request was destroyed 10 minutes ago. You must now save this incoming JSON data using a brand new, completely disconnected DbContext.
When the HTTP PUT request hits your Controller, the JSON body is deserialized into a new C# Product object. However, this object's state inside EF Core is Detached. The database doesn't know if this is a brand new product to INSERT, or an existing product to UPDATE.
The simplest way to handle this is to use the Update() method. This instructs EF Core to blindly attach the object and mark every single property as Modified.
[HttpPut("{id}")]
public async Task<IActionResult> UpdateProduct(int id, Product modifiedProduct)
{
// At this exact moment, modifiedProduct is simply data in RAM. EF Core is oblivious.
// This tells the ChangeTracker: "Assume this exists in the DB, and assume EVERYTHING changed."
_context.Products.Update(modifiedProduct);
// Generates an UPDATE statement targeting ALL columns based on the primary key
await _context.SaveChangesAsync();
return NoContent();
}
Blindly calling Update() is dangerous if the object contains nested lists (e.g., a Blog object containing a list of Posts). If the React app sent a new JSON Array of posts, Update() will try to overwrite the entire database graph, often resulting in primary key violations.
A much safer enterprise approach involves explicitly loading the original entity, mapping the changes via AutoMapper, and saving the tracked result.
[HttpPut("{id}")]
public async Task<IActionResult> SafeUpdate(int id, ProductDto incomingDto)
{
// 1. Fetch the physical original entity (State = Unchanged)
var dbEntity = await _context.Products.FindAsync(id);
// 2. Map only the safe properties from the incoming DTO to the tracked entity
dbEntity.Price = incomingDto.Price;
dbEntity.Name = incomingDto.Name;
// 3. EF Core automatically detects the changes and executes a precise, lightweight UPDATE
await _context.SaveChangesAsync();
return Ok();
}
Q: "When dealing with disconnected entities, why might blindly calling `_context.Update(entity)` result in a severe `InvalidOperationException: The instance of entity type cannot be tracked because another instance with the same key value for {'Id'} is already being tracked.`?"
Architect Answer: "This is the dreaded 'Tracking Conflict' exception. It occurs when your Web API tries to save an incoming untracked object (e.g., ID=5), but somewhere else in that exact same HTTP Request, another piece of code already queried and tracked the ID=5 user (perhaps middleware validating their permissions). EF Core's ChangeTracker dictionary cannot hold two separate C# objects with the exact same Primary Key simultaneously in memory. To solve this, you must either ensure the initial querying code used `.AsNoTracking()`, or use the mapping technique to transfer data onto the already-tracked entity instead of forcefully attaching a secondary object."