Interview question
Handle a rowversion conflict in an update endpoint
Uses the client's original rowversion to detect a lost update and return a deliberate conflict response.
TL;DR
Uses the client's original rowversion to detect a lost update and return a deliberate conflict response.
Concurrency tokens, original values, DbUpdateConcurrencyException, API conflicts, and retry boundaries.
Practice the problem like a real interview: restate, reason, implement, and test.
Order.RowVersion is configured as a SQL Server rowversion concurrency token. The update request includes the base64-decoded original version and a new delivery note. Return not found when the order is absent and conflict when another writer changed it first.
Next in EF Core Labs: Unique Email Conflict
Two users load version A. The first saves and receives version B. The second update still carries A, so its SQL update affects zero rows and the endpoint returns a conflict instead of overwriting the first change.
I load the entity, apply the intended field change, and replace EF's original concurrency value with the version the client actually edited. EF includes that version in the update predicate. A concurrency exception means the row was deleted or changed after the client's read, so I return a conflict and let the user reload or merge deliberately.
public static async Task<UpdateOrderResult> UpdateDeliveryNoteAsync(
AppDbContext db,
Guid orderId,
UpdateDeliveryNote request,
CancellationToken cancellationToken)
{
var order = await db.Orders
.SingleOrDefaultAsync(
candidate => candidate.Id == orderId,
cancellationToken);
if (order is null)
return UpdateOrderResult.NotFound();
order.DeliveryNote = request.DeliveryNote.Trim();
db.Entry(order)
.Property(candidate => candidate.RowVersion)
.OriginalValue = request.RowVersion;
try
{
await db.SaveChangesAsync(cancellationToken);
return UpdateOrderResult.Updated(order.RowVersion);
}
catch (DbUpdateConcurrencyException)
{
return UpdateOrderResult.Conflict(
"The order changed after you opened it. Reload before saving again.");
}
}
EF generates an update constrained by primary key and original rowversion. A matching index already exists through the primary key lookup. Conflict rate is operationally useful: frequent conflicts may reveal a workflow problem rather than a need for blind retries.
Spot a weak answer, missing edge case, or clearer explanation? Send it in.