EF Core 6.0中SET IDENTITY_INSERT ON配置不生效问题排查
问题根因
报错来自两个核心配置/逻辑错误:
IDENTITY_INSERT是会话级生效的SQL Server配置,EF Core默认会将独立调用的ExecuteSqlRaw放在单独的连接会话中执行。你之前分开写的开IDENTITY_INSERT、SaveChanges、关IDENTITY_INSERT三个操作根本不在同一个数据库会话里,等SaveChanges执行时,之前开启的IDENTITY_INSERT已经随连接释放自动关闭,自然会触发IDENTITY_INSERT为OFF的报错。- 实体属性配置逻辑错误:
ValueGeneratedNever()会告知EF Core该列永远不由数据库生成值,会直接导致日常新增业务数据时自增逻辑失效;只配置ValueGeneratedOnAdd()不做额外标记的话,EF Core会默认忽略你传入的TicketId值,强制使用数据库自增生成值,两种配置都不符合你「默认自增、导入时允许手动指定ID」的需求。
正确实现方案
第一步:修正实体列配置
首先调整TicketId的映射配置,明确标记为自增IDENTITY列,适配默认自增的需求:
public void Configure([NotNull] EntityTypeBuilder<Ticket> builder) { builder.HasKey(_ => _.TicketId); builder.Property(e => e.TicketId) .ValueGeneratedOnAdd() .UseIdentityColumn(); // 明确指定使用SQL Server原生IDENTITY自增逻辑,不要配置ValueGeneratedNever() // 其余实体配置保持不变 }
如果之前已经生成过数据库迁移,需要补一个迁移确认TicketId列被正确创建为IDENTITY自增列。
第二步:同会话执行导入逻辑
必须将开启IDENTITY_INSERT、插入数据、关闭IDENTITY_INSERT的操作放在同一个事务、同一个数据库连接内执行,避免会话切换导致配置失效,参考实现代码:
// 开启事务,保证整个导入流程共用同一个数据库连接会话 using var transaction = dbContext.Database.BeginTransaction(); try { // 开启指定表的IDENTITY_INSERT,必须写全schema名,你的表在Corporate架构下就写Corporate.Ticket dbContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT Corporate.Ticket ON"); // 逐个标记导入的Ticket实体:ID是显式指定的,不需要EF Core生成临时值、也不需要数据库自增 foreach (var ticket in importTicketList) // importTicketList是你从供应商API拉取的、带固定TicketId的存量数据集合 { dbContext.Entry(ticket).Property(t => t.TicketId).IsTemporary = false; dbContext.Set<Ticket>().Add(ticket); } // 执行插入 dbContext.SaveChanges(); // 插入完成后关闭IDENTITY_INSERT dbContext.Database.ExecuteSqlRaw("SET IDENTITY_INSERT Corporate.Ticket OFF"); transaction.Commit(); } catch { transaction.Rollback(); throw; }
关于Sequence的说明
不需要使用Sequence。Sequence适合需要跨多张表共享ID生成序列的场景,你当前单表默认自增、仅导入场景手动指定ID的需求,用原生IDENTITY列配合上述实现完全可以满足,额外引入Sequence只会增加不必要的维护成本。
报错排查清单
如果调整后依然报错,逐一核对以下项:
- 核对
IDENTITY_INSERT后的表名是否带正确的schema前缀,漏写Corporate.会导致SQL Server操作dbo schema下的同名表,配置没有落到实际业务表上 - 确认开启IDENTITY_INSERT到SaveChanges之间没有触发EF Core的连接回收逻辑,不要在这两步之间执行其他会释放连接的操作
- 连库执行以下SQL确认表结构配置正确,返回值为1才说明TicketId是合法的IDENTITY列:
SELECT COLUMNPROPERTY(OBJECT_ID('Corporate.Ticket'),'TicketId','IsIdentity')
如果返回0,说明之前的迁移脚本没有正确配置自增属性,修正迁移后重新更新表结构即可。
内容的提问来源于stack exchange,提问作者malathyr
相关产品推荐
相关产品推荐

