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

调用SaveChanges时触发EF乐观并发错误,求排查解决思路

排查与修复建议

针对你遇到的EF批量增改时触发DbUpdateConcurrencyException且目标表无数据的问题,以下是具体的排查方向和修复方案:

1. 实体缺失主键导致EF无法定位更新目标

你的Record实体未定义主键,EF无法唯一标识实体,导致Update操作无法确定数据库中对应的行,最终触发影响0行的异常。业务上UserId和CourseId是唯一标识一条记录的字段,需配置为复合主键:

public class Record
{
    [Key, Column(Order = 0)]
    public int UserId { get; set; }

    [Key, Column(Order = 1)]
    public int CourseID { get; set; }

    [ConcurrencyCheck]
    public int CourseSessionID { get; set; }

    public string Code { get; set; }

    [ConcurrencyCheck]
    public DateTime DateFrom { get; set; }

    [ConcurrencyCheck]
    public DateTime DateTo { get; set; }
}

2. 同一批次重复记录引发的上下文状态冲突

如果文件中存在相同UserId+CourseId的多行记录,第一行被Add到上下文后处于Added状态;第二行通过GetRecord从上下文中获取该实体,调用Update会将其状态改为Modified。SaveChanges时EF会执行UPDATE语句,但此时数据库中尚无该记录,导致影响行数为0触发异常。

修复:先对文件记录按UserId+CourseId去重,保留最新的记录:

var uniqueFileRecords = fileRecords
    .GroupBy(r => new { r.UserId, r.CourseId })
    .Select(g => g.OrderByDescending(r => DateTime.ParseExact(r.DateFrom, "yyyyMMdd", CultureInfo.InvariantCulture)).First())
    .ToList();

foreach (var record in uniqueFileRecords)
{
    // 原循环处理逻辑
}

3. 冗余的Update调用导致状态混乱

从上下文获取的existingRecord已被EF跟踪,修改属性后EF会自动记录变化,无需调用Update方法(Update用于附加未被跟踪的实体)。冗余调用会强制将实体状态设为Modified,干扰EF的自动跟踪逻辑。

修复:修改UpdateRecord方法,移除Update调用:

private void UpdateRecord(Data.Entities.Record existingRecord, FileRecord updatedRecord)
{
    existingRecord.CourseSessionId = int.Parse(updatedRecord.CourseSessionId);
    existingRecord.DateFrom = string.IsNullOrWhiteSpace(updatedRecord.DateFrom) 
        ? new DateTime(1900, 1, 1) // 替换为PostgreSQL支持的最小日期
        : DateTime.ParseExact(updatedRecord.DateFrom, "yyyyMMdd", CultureInfo.InvariantCulture);
    existingRecord.DateTo = string.IsNullOrWhiteSpace(updatedRecord.DateTo) 
        ? new DateTime(1900, 1, 1) 
        : DateTime.ParseExact(updatedRecord.DateTo, "yyyyMMdd", CultureInfo.InvariantCulture);
}

4. DateTime.MinValue与PostgreSQL日期兼容性问题

PostgreSQL的DATE类型不支持DateTime.MinValue(0001-01-01),如果将DateFrom/DateTo设为该值,插入操作会失败,导致记录无法写入数据库,后续更新自然找不到对应行。

修复:改用PostgreSQL支持的最小日期(如1900-01-01)替换DateTime.MinValue。

5. 开启EF日志排查SQL语句

通过日志查看EF生成的SQL语句,确认执行的是插入还是更新操作,以及WHERE条件是否正确,进一步定位问题:

// 在Startup.cs的DbContext配置中添加日志
builder.Services.AddDbContext<ESRContext>(options => 
    options.UseNpgsql(Environment.GetEnvironmentVariable("PostgreDBConnectionString"))
        .LogTo(Console.WriteLine, LogLevel.Information));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 15:39:15