在 C# 使用 Entity Framework Core 进行数据持久化时,如果启用了乐观并发控制,当两次提交基于不同数据版本修改同一记录,后提交的一方就会抛出 DbUpdateConcurrencyException。该异常表明上下文所持有的原始值与数据库现有值不一致,保存操作被拒绝。

异常产生的原因
EF Core 在生成 UPDATE 语句时会把并发令牌(如 rowversion 或额外标记列)作为 WHERE 条件的一部分。若数据库中对应行已被其他操作更改,令牌不匹配,影响行数为零,框架便抛出 DbUpdateConcurrencyException。
基础捕获与重试策略
最通用的做法是在 SaveChanges 时捕获异常,重新从数据库拉取最新数据,再合并用户改动后重试。以下示例展示了一个简单的处理模式:
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
public class ProductService
{
private readonly AppDbContext _ctx;
public ProductService(AppDbContext ctx)
{
_ctx = ctx;
}
public void UpdatePrice(int id, decimal newPrice)
{
// 最多重试三次
for (int i = 0; i < 3; i++)
{
try
{
var product = _ctx.Products.Find(id);
if (product == null) return;
product.Price = newPrice;
_ctx.SaveChanges();
return;
}
catch (DbUpdateConcurrencyException ex)
{
// 重新加载数据库中的当前值
foreach (var entry in ex.Entries)
{
entry.Reload();
}
}
}
throw new Exception("并发冲突过多,更新失败");
}
}
对比数值并提示用户
在业务场景中,有时需要把数据库当前值、原始值与修改值都展示给用户,由用户决定如何处置。可通过以下方式获取三方值:
- 当前值:entry.CurrentValues
- 原始值:entry.OriginalValues
- 数据库值:entry.GetDatabaseValues()
示例代码如下:
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var dbValues = entry.GetDatabaseValues();
if (dbValues != null)
{
// 将数据库值写入原始值,避免下次再冲突
entry.OriginalValues.SetValues(dbValues);
// 此处可将 dbValues 与 entry.CurrentValues 展示给用户界面
}
}
配置并发令牌
要在模型中启用乐观锁,需要显式标记并发列。对于 SQL Server 可使用 rowversion:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
// 并发令牌
public byte[] RowVersion { get; set; }
}
// 在 DbContext 的 OnModelCreating 中
modelBuilder.Entity<Product>()
.Property(p => p.RowVersion)
.IsRowVersion();
处理建议总结
| 策略 | 适用场景 |
|---|---|
| 自动重载重试 | 后台任务或低冲突频率 |
| 提示用户合并 | 前台交互式编辑 |
| 放弃本地修改 | 以数据库为准的最终一致 |
合理运用上述方式,可让程序在遭遇 DbUpdateConcurrencyException 时既保证数据安全,也维持良好的可用性。
C#DbUpdateConcurrencyExceptionEF_Core并发控制乐观锁修改时间:2026-07-25 05:09:20