单事务插入父子表记录时提交多对象出现CameraActivity实体跟踪错误如何解决
EF Core 单事务插入父子关联表跟踪异常解决方案
报错信息
实体类型“CameraActivity”的实例无法被跟踪,因为键值为{'ActivityId', 'FeatureId', 'CamId'}的另一个实例已被跟踪。
附加现有实体时,请确保仅附加具有给定键值的一个实体实例。
可使用'DbContextOptionsBuilder.EnableSensitiveDataLogging'查看冲突的键值。
问题根因
- 联合主键重复赋值:你当前代码的
foreach循环将所有CameraActivity子对象的ActivityId、FeatureId统一设置为集合首个元素的值,加上所有子项的CamId均为同一个值,直接导致所有子实体的联合主键{ActivityId, FeatureId, CamId}完全重复,既违反数据库主键唯一约束,也会触发EF Core的实体跟踪冲突。 - 实体重复跟踪:调用
_context.Cameras.Add(camera)时,EF Core会自动级联跟踪camera对象关联的CameraActivities导航属性中的所有子实体,后续再手动调用_context.CameraActivities.AddRange相当于重复添加同一批实体,进一步触发跟踪冲突。 - 外键赋值时机错误:如果
CamId是数据库自增主键,在调用SaveChanges之前,camera.CamId还是默认空值/0,此时手动给子项赋值CamId本身就是无效值。
修复方案
EF Core原生支持级联插入关联数据,只要你在模型配置中正确定义了Camera和CameraActivity的一对多导航关系,不需要手动处理子项的外键赋值、也不需要手动添加子实体到上下文,修改后代码如下:
public async Task<ActionResult<Camera>> PostCamera(Camera camera) { var cameraRecodExists = _context.Cameras.FirstOrDefault(C => C.IPAdress == camera.IPAdress) ; if (cameraRecodExists !=null) { return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = "Record Already Exists" }); } // 仅保留业务需要的赋值逻辑,必须保证每个CameraActivity的{ActivityId, FeatureId}组合唯一,避免联合主键重复 foreach (var activity in camera.CameraActivities) { // 示例:根据实际业务赋值,不要统一设置为集合首个元素的ActivityId和FeatureId // activity.ActivityId = 业务逻辑返回的唯一值 // activity.FeatureId = 业务逻辑返回的唯一值 } // 仅需添加父实体,EF会自动级联跟踪处理子实体 _context.Cameras.Add(camera); try { await _context.SaveChangesAsync(); } catch(Exception ex) { return StatusCode(StatusCodes.Status500InternalServerError, new Response { Status = "Error", Message = ex.Message }); } return CreatedAtAction("GetCamera", new { id = camera.CamId }, camera); }
补充说明
如果业务上确实需要同一个摄像头下存在相同ActivityId和FeatureId的记录,只需要修改CameraActivity的模型配置,新增独立自增主键字段(比如Id),取消{ActivityId, FeatureId, CamId}的联合主键配置即可。
内容的提问来源于stack exchange,提问作者Abubakar Siddeque
相关产品推荐
相关产品推荐

