What is optimistic concurrency in EF Core, and how do you implement it?

Entity Framework & EF Core Persistence Advanced 3–5 Yrs5+ Yrs Conceptual 1 min read

Quick Answer

Optimistic concurrency assumes conflicts are rare: you don't lock a row while a user edits it. Instead you add a concurrency token (a rowversion/timestamp column). On SaveChanges, EF adds WHERE Id = @id AND RowVersion = @original; if another user changed the row, 0 rows update and EF throws DbUpdateConcurrencyException.

Optimistic concurrency bets that two people editing the same row at the same time is rare, so it doesn't hold a lock. I add a rowversion column as a concurrency token. When EF saves, it includes the original rowversion in the WHERE clause, so if someone else updated the row in the meantime, zero rows match, and EF raises DbUpdateConcurrencyException. Then I handle it — usually reload the current values and ask the user to review, or apply a last-write-wins or first-write-wins rule depending on what the business wants. It stops the silent lost-update where the second save overwrites the first.

  • The problem it solves: the lost update — two users load the same record, both save, the second silently overwrites the first.
  • Setup: a byte[] RowVersion property with [Timestamp] or .IsRowVersion(). SQL Server auto-increments it on every update.
  • On conflict: catch DbUpdateConcurrencyException, then decide — reload and let the user merge, take the database version, or force yours. There's no universal answer; it's a business decision.
  • You can also mark specific properties as concurrency tokens (.IsConcurrencyToken()) if you don't want a rowversion column.
  • Contrast with pessimistic concurrency (locking with UPDLOCK), which EF doesn't do natively and which hurts scalability.