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

使用EF Core插入数据时触发SqlException与DbUpdateException问题求助

EF Core插入操作主键重复错误排查与解决

问题说明

执行以下EF Core代码插入数据时,触发SqlException和DbUpdateException:

var executionStrategy = _companyContext.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(async () =>
{
    using var transaction = await _companyContext.Database.BeginTransactionAsync();
    _companyContext.Set<CARILER_T>().Add(newEntity);
    _companyContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT CARILER_T ON;");
    await _companyContext.SaveChangesAsync();
    _companyContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT CARILER_T OFF");
    await transaction.CommitAsync();
});

错误详情:

SqlException: 违反PRIMARY KEY约束'PK_CARILER_T'。无法在对象'dbo.CARILER_T'中插入重复键。重复键值为(536154)。
语句已终止。

DbUpdateException: 保存实体更改时发生错误。
请参阅内部异常了解详细信息。

原因分析

  1. 目标ID已存在:newEntity的ID值(536154)已经在CARILER_T表中存在,开启IDENTITY_INSERT后EF会直接使用该ID执行插入,触发主键约束冲突。
  2. 执行策略重试导致重复插入:EF Core的执行策略(ExecutionStrategy)会在遇到临时数据库错误时重试整个逻辑块。如果第一次插入成功,后续重试时newEntity的ID未变更,会再次尝试插入同一ID,导致重复。
  3. 实体主键配置不匹配:若实体的主键未配置为DatabaseGeneratedOption.None,EF可能仍期望数据库自动生成ID,开启IDENTITY_INSERT后插入逻辑出现冲突。

解决办法

1. 插入前校验ID唯一性

在插入前查询表中是否已存在目标ID,避免硬插入重复值:

var exists = await _companyContext.Set<CARILER_T>().AnyAsync(c => c.Id == newEntity.Id);
if (!exists)
{
    // 执行插入逻辑
}
else
{
    // 处理重复情况:跳过/更新现有记录
}

2. 处理执行策略的幂等性问题

如果必须使用执行策略,确保重试逻辑是幂等的:

  • 在重试块内重新确认记录是否已存在,存在则直接提交事务,不再执行插入:
await strategy.ExecuteAsync(async () =>
{
    using var transaction = await _companyContext.Database.BeginTransactionAsync();
    var exists = await _companyContext.Set<CARILER_T>().AnyAsync(c => c.Id == newEntity.Id);
    if (!exists)
    {
        _companyContext.Set<CARILER_T>().Add(newEntity);
        _companyContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT CARILER_T ON;");
        await _companyContext.SaveChangesAsync();
        _companyContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT CARILER_T OFF");
    }
    await transaction.CommitAsync();
});
  • 若ID不需要固定,改为由数据库自动生成,移除IDENTITY_INSERT相关代码,并确保实体主键配置为DatabaseGeneratedOption.Identity(默认配置)。

3. 修正实体主键配置

如果确实需要手动指定ID,在实体类或Fluent API中明确配置主键生成策略:

  • 数据注解方式:
public class CARILER_T
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
    // 其他属性
}
  • Fluent API方式:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<CARILER_T>()
        .Property(c => c.Id)
        .ValueGeneratedNever();
}

4. 优化事务内操作顺序

将SET IDENTITY_INSERT ON语句提前到Add操作之前,确保EF生成INSERT语句时包含ID字段:

await strategy.ExecuteAsync(async () =>
{
    using var transaction = await _companyContext.Database.BeginTransactionAsync();
    _companyContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT CARILER_T ON;");
    _companyContext.Set<CARILER_T>().Add(newEntity);
    await _companyContext.SaveChangesAsync();
    _companyContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT CARILER_T OFF");
    await transaction.CommitAsync();
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 05:35:27