EF Core 乐观锁重试需捕获 DbUpdateConcurrencyException 后刷新 OriginalValues 并重试,前提是正确配置并发令牌(如 [Timestamp] 或 Fluent API 的 IsRowVersion),否则不触发校验。

EF Core 实现乐观锁重试,核心是捕获 DbUpdateConcurrencyException 后主动刷新原始值并再次提交。它不是开箱即用的“自动重试”,而是需要你显式编写重试逻辑——但结构清晰、可控性强。
配置并发令牌是前提
没有正确配置并发令牌,EF Core 就不会触发版本校验,自然也不会抛出并发异常。必须确保至少一个属性被标记为并发令牌:
- 推荐用
[Timestamp]+byte[]类型(如RowVersion),SQL Server 自动维护,高效可靠 - 也可用
[ConcurrencyCheck]标记任意字段(如LastUpdated或Version),但需业务层保证更新时同步赋值 - Fluent API 配置更灵活:
modelBuilder.Entity().Property(x => x.RowVersion).IsRowVersion();
手动重试:捕获 → 刷新 → 再保存
这是最常用、最可控的方式。关键在于调用 entry.OriginalValues.SetValues(databaseValues),让 EF Core 下次比较时用数据库最新值作为“原始值”:
- 在
catch (DbUpdateConcurrencyException ex)中遍历ex.Entries - 对每个条目调用
entry.GetDatabaseValues()获取当前库值 - 用
entry.OriginalValues.SetValues(...)覆盖原始快照 - 最后再调用
context.SaveChanges()尝试第二次提交
封装成可重试方法(带次数限制)
避免重复写 try-catch,可抽成通用方法。例如:
- 定义重试次数(如最多 3 次),每次失败后延迟递增(如 100ms、200ms、400ms)
- 每次重试前重新查询实体(或用
AsNoTracking().FirstOrDefault()获取最新状态) - 若仍失败,可抛出带上下文信息的自定义异常,或返回失败标识供上层处理
- 注意:不要在同一个 DbContext 实例里无限重试,建议每次重试用新上下文或显式
Reload()
用 Polly 库实现声明式重试策略
如果你项目已引入 Polly,可以简洁地表达重试意图:
Policy.Handle().WaitAndRetry(3, i => TimeSpan.FromMilliseconds(Math.Pow(2, i) * 100)) - 把
context.SaveChanges()包进策略执行块中,失败自动重试 - 配合
onRetry回调,在每次重试前刷新实体原始值,保持逻辑完整
基本上就这些。重试本身不复杂,但容易忽略原始值刷新这一步——没它,重试只是反复拿旧快照去比,永远失败。










