You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用DateTime字段处理Entity Framework并发时遇DbUpdateConcurrencyException问题

解决DateTime精度不匹配导致的DbUpdateConcurrencyException问题

这个问题我之前处理过好几次,核心原因就是数据库存储的DateTime带毫秒精度,但EF读取实体时截断了这部分信息,导致更新时用截断后的LastModificationTime和数据库里的完整时间对比,触发并发冲突。

为什么会出现这个情况?

SQL Server的datetime2类型支持最高7位的毫秒精度(比如你数据库里的2020-03-09 10:02:37.4570000),但EF默认映射DateTime类型时,可能没有保留足够的精度,或者你的实体属性没有明确指定对应的数据库列类型,导致读取时丢失了毫秒部分。这样一来,你获取的intervenant.LastModificationTime是2020-03-09T10:02:37(无毫秒),和数据库实际值对比自然不相等,触发DbUpdateConcurrencyException。

解决方案

1. 统一实体与数据库的DateTime精度(最根本的解决办法)

你需要确保实体的LastModificationTime属性和数据库列的精度完全匹配。可以通过两种方式配置:

方式一:使用数据注解

直接在实体属性上添加[Column]特性,指定数据库列类型为datetime2(7)(对应最高精度):

[ConcurrencyCheck]
[Column(TypeName = "datetime2(7)")]
public DateTime LastModificationTime { get; set; }
方式二:使用Fluent API(推荐,更灵活)

在DbContext的OnModelCreating方法中配置属性的精度:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Intervenant>()
        .Property(p => p.LastModificationTime)
        .IsConcurrencyToken() // 等效于[ConcurrencyCheck]
        .HasColumnType("datetime2(7)") // 指定列类型和精度
        .HasPrecision(7); // EF Core 5+可以直接指定精度,无需写ColumnType
}

配置完成后,记得生成并执行数据库迁移,确保数据库列类型更新为datetime2(7),这样EF读取和写入时都会保留完整的毫秒精度,对比时就不会出现不一致了。

2. 确保查询获取最新的完整精度值

有时候,EF的一级缓存可能会返回之前读取的截断了毫秒的实体。你可以在查询时跳过缓存,强制从数据库获取最新数据:

// 用Find方法(会绕过一级缓存,直接查数据库)
var intervenant = this.IntervenantRepository.Find(intervenantId);

// 或者在EF中使用AsNoTracking后再跟踪(适用于EF Core)
var intervenant = this.IntervenantRepository.GetAll()
    .AsNoTracking()
    .FirstOrDefault(intervenant => intervenant.Id == intervenantId);
this.IntervenantRepository.Attach(intervenant); // 重新跟踪实体

3. 处理并发异常的兜底方案

如果偶尔还是会出现并发冲突(比如多人同时修改),可以捕获DbUpdateConcurrencyException,重新读取最新实体,合并修改后再次尝试更新:

try
{
    var intervenant = this.IntervenantRepository.GetAll().FirstOrDefault(intervenant => intervenant.Id == intervenantId);
    if (IsInscrire)
    {
        intervenant.MotifdesinscId = null;
        intervenant.IsInscrit = true;
    }
    this.IntervenantRepository.Update(intervenant);
    this.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
    // 获取数据库中的最新实体
    var entry = ex.Entries.Single();
    var databaseValues = entry.GetDatabaseValues();
    var intervenantDb = databaseValues.ToObject<Intervenant>();
    
    // 合并修改:保留用户的修改,更新LastModificationTime为最新值
    intervenantDb.MotifdesinscId = intervenant.MotifdesinscId;
    intervenantDb.IsInscrit = intervenant.IsInscrit;
    
    // 更新实体并重新保存
    entry.OriginalValues.SetValues(databaseValues);
    entry.CurrentValues.SetValues(intervenantDb);
    this.SaveChanges();
}

总结

优先解决精度匹配的问题(方案1),这是从根源上避免并发异常的方法;方案2和3作为辅助,分别解决缓存问题和处理极端情况下的并发冲突。

内容的提问来源于stack exchange,提问作者ucef

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 22:32:44