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

SQL Server/Entity Framework触发重复异常后如何继续插入数据

问题根因

EF Core 的 DbContext 触发 SaveChanges 异常后,上下文不会自动重置状态,之前添加的、触发唯一索引冲突的实体仍然被 ChangeTracker 追踪。后续执行 SaveChanges 时,EF Core 仍会把所有被追踪的待提交实体一起发送到数据库,哪怕后续新增的是合法数据,之前残留的冲突实体还是会触发重复键异常,导致后续插入全部失败。

你的代码还有两个细节问题:

  • AddResultAsync 方法中调用 Context.Results.AddAsync(result) 没有等待返回的ValueTask,且普通实体新增不需要调用异步Add方法,直接用同步Add即可
  • 无论插入成功还是失败,你都把newResult1加入了返回结果列表results,最终返回的结果会包含插入失败的重复数据
修复方案

方案1:异常时分离冲突实体(改动最小)

在catch块中检测到唯一键冲突异常时,把当前触发错误的实体从ChangeTracker中移除,避免后续提交时带上该脏数据。修改后的循环逻辑如下:

for (var j = 0; j < resultDetails.Count(); j++)
{
    var rd = resultDetails.ElementAt(j);

    var newResult1 = new Result
    {
        AthleteFEIID = rd.AthleteFEIID,
        CompetitionCode = competition.CompetitionCode,
        HorseId = horse.Id,
    };

    try
    {
        _resultsService.Context.Results.Add(newResult1);
        await _resultsService.CompleteAsync();
        // 插入成功再加入返回列表
        results.Add(newResult1);
    }
    catch (DbUpdateException ex) when (ex.InnerException is SqlException sqlEx && (sqlEx.Number == 2601 || sqlEx.Number == 2627))
    {
        // 2601、2627 是SQL Server唯一索引/唯一约束冲突的固定错误号
        // 把冲突实体从上下文追踪中分离,清除脏状态
        _resultsService.Context.Entry(newResult1).State = EntityState.Detached;
        // 此处可按需添加重复数据日志逻辑
    }
}

同时修正服务层的新增方法,去掉不必要的异步Add调用:

public Task AddResultAsync(Result result)
{
    Context.Results.Add(result);
    return Task.CompletedTask;
}

方案2:单条数据使用独立DbContext(隔离性最好)

如果使用依赖注入容器,可以在循环内部为每一条数据创建独立的DbContext作用域,单条数据插入失败直接销毁对应作用域,完全不会污染其他插入操作的上下文状态,适合对数据隔离要求高的场景。

性能优化建议

如果批量插入的数据量较大,循环内单次提交的性能很差,且依赖异常捕获做重复判断的效率很低。可以在插入前先根据唯一索引对应的三个字段,一次性查询数据库中已存在的记录,在内存中先过滤掉重复项,再批量提交剩余合法数据,能大幅减少数据库交互次数和异常触发开销。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 21:00:54