dotnet
data_error
ai_generated
true
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded.
ID: dotnet/ef-core-savechanges-optimistic-concurrency
80%Fix Rate
86%Confidence
1Evidence
2023-09-12First Seen
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 6.0 | active | — | — | — |
| 7.0 | active | — | — | — |
| 8.0 | active | — | — | — |
| 9.0 | active | — | — | — |
Root Cause
Optimistic concurrency conflict occurs when another user or process modifies or deletes the same row between the time the entity was loaded and SaveChanges is called, and the concurrency token (e.g., RowVersion) does not match.
generic中文
当另一个用户或进程在加载实体和调用 SaveChanges 之间修改或删除了同一行,并且并发令牌(如 RowVersion)不匹配时,会发生乐观并发冲突。
Official Documentation
https://learn.microsoft.com/en-us/ef/core/saving/concurrencyWorkarounds
-
85% success Implement a retry loop that catches DbUpdateConcurrencyException, refreshes the entity from the database using 'await context.Entry(entity).ReloadAsync()', and then reapplies changes before calling SaveChanges again.
Implement a retry loop that catches DbUpdateConcurrencyException, refreshes the entity from the database using 'await context.Entry(entity).ReloadAsync()', and then reapplies changes before calling SaveChanges again.
-
80% success Use a database-first approach with a timestamp column: add a byte[] property annotated with '[Timestamp]' to the entity, and ensure all updates include the original timestamp value in the WHERE clause.
Use a database-first approach with a timestamp column: add a byte[] property annotated with '[Timestamp]' to the entity, and ensure all updates include the original timestamp value in the WHERE clause.
中文步骤
Implement a retry loop that catches DbUpdateConcurrencyException, refreshes the entity from the database using 'await context.Entry(entity).ReloadAsync()', and then reapplies changes before calling SaveChanges again.
Use a database-first approach with a timestamp column: add a byte[] property annotated with '[Timestamp]' to the entity, and ensure all updates include the original timestamp value in the WHERE clause.
Dead Ends
Common approaches that don't work:
-
70% fail
Disabling concurrency tokens by removing RowVersion properties allows overwrites without conflict detection, but can lead to lost updates and data corruption in multi-user scenarios.
-
80% fail
Retrying the entire transaction without refreshing the entity from the database will still use the stale concurrency token, causing the same error repeatedly.