使用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: 保存实体更改时发生错误。
请参阅内部异常了解详细信息。
原因分析
- 目标ID已存在:
newEntity的ID值(536154)已经在CARILER_T表中存在,开启IDENTITY_INSERT后EF会直接使用该ID执行插入,触发主键约束冲突。 - 执行策略重试导致重复插入:EF Core的执行策略(ExecutionStrategy)会在遇到临时数据库错误时重试整个逻辑块。如果第一次插入成功,后续重试时
newEntity的ID未变更,会再次尝试插入同一ID,导致重复。 - 实体主键配置不匹配:若实体的主键未配置为
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
相关产品推荐
相关产品推荐

