EF Core Edit控制器下将_context.Update改为Add新增记录的实现方法
EF Core Edit 场景下复用传入实体实现新增而非更新的方案
问题根因
直接将原有_context.update(datarecord)替换为_context.add(datarecord)失败的核心原因是:当前DbContext实例已经追踪了主键与传入实体一致的旧记录,EF Core变更追踪器默认会将携带已追踪主键值的实体识别为待更新对象;同时主键为数据库自动生成的标识列,直接手动修改主键值会触发Cannot insert explicit value for identity column in table 'table' when IDENTITY_INSERT is set to OFF报错,修改数据库配置开启IDENTITY_INSERT属于非规范实现,完全不推荐。
推荐实现方案
方案1:修改实体追踪状态(代码改动最小,适配现有控制器逻辑)
不需要新建实体、不需要修改数据库配置,只需要在调用Add前显式分离传入实体的追踪状态,让EF Core将其识别为全新未追踪对象即可:
// 先将传入实体标记为分离状态,切断与原有已追踪记录的关联 _context.Entry(datarecord).State = EntityState.Detached; // 若主键为可空类型(int?/Guid?),可显式将主键赋值为null,进一步确保EF Core识别为新实体 // 不可空主键不需要手动赋值0/空Guid,EF Core会自动生成新主键值 // datarecord.keyxxx = null; // 执行新增,提交时EF Core会自动生成新主键,不会更新原有记录 _context.Add(datarecord); await _context.SaveChangesAsync();
若实体带有关联导航属性,不需要额外调整导航属性的追踪状态,EF Core执行Add时会按照约定自动处理关联关系,不会误更新原有关联数据。
方案2:新建实体映射属性(最稳妥,完全规避追踪冲突)
如果Edit方法内已经执行过多次数据查询、上下文追踪的实体较多,担心状态调整引发非预期问题,可以直接新建空实体实例,拷贝传入实体的业务属性后再执行新增,完全避开原有追踪的影响:
var newDataRecord = new DataRecord(); // 拷贝传入实体的所有属性值到新实例 _context.Entry(newDataRecord).CurrentValues.SetValues(datarecord); // 清空新实例的主键,避免带入原有主键值 newDataRecord.keyxxx = 0; // Guid类型主键赋值Guid.Empty,可空类型赋值null _context.Add(newDataRecord); await _context.SaveChangesAsync();
不推荐开启IDENTITY_INSERT的原因
- 该方案的逻辑是允许手动插入主键值,和“自动生成新主键、保留原有记录”的业务需求完全相悖
- 开启IDENTITY_INSERT需要表级ALTER权限,生产环境权限管控严格时无法落地
- 高并发场景下手动指定主键极易触发主键冲突,稳定性没有保障
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

