EF Core报实体类型实例无法被跟踪异常的原因及优化方案咨询
问题1:异常抛出的具体原因
你观测到的异常行号存在偏差,var addingSomeEntity = new List<updatingEntity>(); 仅为泛型列表初始化逻辑,本身不会触发EF Core的实体跟踪操作,异常实际由后续的AddRangeAsync调用触发,核心原因如下:
- EF Core上下文的变更追踪器默认会对同个键值的实体仅保留一个跟踪实例,你前面调用
UpdateRange方法时,所有传入的UpdateEntity实体已经被加入跟踪队列 - 你代码存在多处笔误放大了冲突风险:
UpdateRange的参数错误写为类型名UpdateEntity,实际应该传入存储待更新实体的变量updatingEntity- 初始化
addingSomeEntity时泛型参数错误写为变量名updatingEntity,实际应该传入实体类型UpdateEntity - 插入逻辑的循环中,错误将待新增的
newItem加入了更新用的updatingEntity列表,而非插入用的addingSomeEntity列表,导致待新增的实体和待更新的实体被塞进同一个列表
- 最终执行
AddRangeAsync时,传入的实体集合中存在和已跟踪实体Id完全相同的实例,变更追踪器无法同时跟踪两个同Id的同类型实体,因此抛出The instance of entity type 'xxx' cannot be tracked异常
问题2:是否存在代码正常运行不抛异常的场景
存在,满足任意以下条件即可正常运行:
- 前序的待更新列表
updatingEntity为空,UpdateRange未执行,上下文没有跟踪任何UpdateEntity实例 - 待插入的
newList中所有实体的Id,和前序updatingEntity里的实体Id完全无重复,不存在跟踪冲突 - 前序获取
List时使用了无跟踪查询,且上下文关闭了自动附加跟踪功能,UpdateRange执行后没有将实体加入跟踪队列 - 两次操作之间手动调用了
DbContext.ChangeTracker.Clear()清空了所有已跟踪的实体
问题3:可行的优化方案
可根据业务场景任选以下方案解决:
- 方案1:操作完成后清空跟踪队列
执行完更新逻辑并调用SaveChanges后,新增一行DbContext.ChangeTracker.Clear()清空所有已跟踪的实体,再执行后续插入逻辑,从根源避免跟踪冲突 - 方案2:拆分更新/插入逻辑,避免实体重复
插入前先过滤newList,把和updatingEntity列表中Id重复的实体筛选出来走更新逻辑,剩余无重复的实体走插入逻辑,完全避免同Id实体同时被跟踪 - 方案3:显式指定实体状态,跳过自动附加
不需要EF Core自动管理跟踪状态的场景下,可手动指定实体状态:待更新实体设置DbContext.Entry(entity).State = EntityState.Modified,待插入实体设置DbContext.Entry(entity).State = EntityState.Added,绕过自动附加的冲突校验 - 方案4:修复代码笔误,避免逻辑混淆
修正前文提到的3处代码笔误,更新和插入逻辑各自使用独立的实体列表,不要混用变量,避免不必要的实体重复
内容的提问来源于stack exchange,提问作者cuong nguyen manh
相关产品推荐
相关产品推荐

