Product实体跟踪冲突求助:更新代码抛出EF Core跟踪异常
嘿,这个问题我太熟了!你遇到的是EF Core实体跟踪机制的经典冲突问题,我来给你拆解清楚并解决它:
问题根源
你先通过FirstOrDefaultAsync查询出了existing实体,此时EF Core的上下文已经在跟踪这个实例了;之后你用ObjectMapper.Map<Product>(input)创建了一个全新的Product实例——它和existing拥有完全相同的主键Id。当你调用UpdateAsync时,上下文发现有两个主键相同但实例不同的被跟踪对象,自然就抛出冲突异常了。
下面给你几种可行的解决思路,按推荐程度排序:
方案1:直接更新已跟踪的实体(最推荐)
既然existing已经被上下文跟踪了,我们完全没必要创建新实例,直接把input的属性映射到existing上就行。EF Core会自动跟踪属性的变化,最后保存时只会更新实际修改的字段,性能更好还避免了冲突。代码修改如下:
var existing = await _productRepository.FirstOrDefaultAsync(c => c.Id == input.Id); if (existing == null) throw new UserFriendlyException(L("ProductNotExist")); // 把input的属性映射到已有的existing实例上,而非创建新实例 ObjectMapper.Map(input, existing); // 因为existing已经被上下文跟踪,直接调用UpdateAsync即可(ABP框架会自动处理变更保存) var entity = await _productRepository.UpdateAsync(existing);
方案2:停止跟踪原实体后再更新新实例
如果你确实有场景需要创建新的updatedEntity实例,可以先让上下文停止跟踪existing,这样再更新新实例就不会有冲突了:
var existing = await _productRepository.FirstOrDefaultAsync(c => c.Id == input.Id); if (existing == null) throw new UserFriendlyException(L("ProductNotExist")); // 停止跟踪原实体——ABP框架可以通过DbContext操作 _dbContext.Entry(existing).State = EntityState.Detached; var updatedEntity = ObjectMapper.Map<Product>(input); var entity = await _productRepository.UpdateAsync(updatedEntity);
注意:如果你的Repository封装了Detach方法,也可以直接调用_productRepository.Detach(existing),效果是一样的。这个方案适合特殊场景,但不如方案1优雅。
方案3:查询时禁用跟踪
如果你查询existing只是为了验证是否存在,根本不需要跟踪它,可以在查询时加上AsNoTracking(),这样上下文就不会跟踪这个实例,后续创建新实例更新就不会冲突了:
// 查询时禁用跟踪,ABP的Repository可以通过includeDetails参数控制,或者直接用EF Core原生方法 var existing = await _productRepository.FirstOrDefaultAsync(c => c.Id == input.Id, includeDetails: false); // 或者原生EF写法:var existing = await _dbContext.Products.AsNoTracking().FirstOrDefaultAsync(c => c.Id == input.Id); if (existing == null) throw new UserFriendlyException(L("ProductNotExist")); var updatedEntity = ObjectMapper.Map<Product>(input); var entity = await _productRepository.UpdateAsync(updatedEntity);
这个方案只适合查询仅用于存在性验证的场景,如果你之后还要用到existing的属性值,就不太适用了。
总结一下,方案1是最优解,它完全符合EF Core的变更跟踪设计,代码简洁还能提升性能。
内容的提问来源于stack exchange,提问作者Edward

