EF Core报错:无法跟踪实体实例,同ID实例已被跟踪
问题分析与解决
错误信息
无法跟踪该实体类型的实例,因为已存在另一个具有相同键(id = "2")的同类型实例正在被跟踪。
问题描述
我在更新FeatureDisa实体时遇到上述错误,不清楚原因。另外我不能使用AsNoTracking(),因为需要记录日志。
相关代码
查询与更新逻辑
var updateFeatureDisas = await _featureDisaRepository. GetAll() .Where(a => featureDisaIdInsert.Contains(a.DisadvantageId) && featureIdInsert.Contains(a.FeatureId) && featureParentIdInsert.Contains(a.ParentFeatureId)).ToListAsync(); var sqInsertFeatureDisa = insertTagDisa.Where(a => updateFeatureDisas.All(d => d.Id != a.Id)).ToList(); if (updateFeatureDisas.Any()) { var sqUpdateFeatureDisa = insertTagDisa.Where(a => updateFeatureDisas. All(d => d.Id.Equals(a.Id, StringComparison.Ordinal))).ToList(); var updateFeatureDisa = _mapper.Map<List<FeatureDisa>>(sqUpdateFeatureDisa); _featureDisaRepository.UpdateRangeWithOutSaveChangeAsync(updateFeatureDisa); }
仓储更新方法
public void UpdateWithOutSaveChangeAsync(TEntity entity) { DbContext.Update(entity); }
原因与解决方案
错误原因
你从数据库查询出的updateFeatureDisas集合已经被EF上下文跟踪,之后通过AutoMapper生成了新的updateFeatureDisa实例(这些实例与已跟踪实体的ID完全相同)。调用DbContext.Update()时,EF检测到同一个ID的实体已在跟踪列表中,因此抛出冲突错误。
解决方案(二选一)
方案1:直接修改已跟踪实体(推荐)
无需创建新实例,直接将新值映射到已被EF跟踪的实体上,EF会自动识别变更并标记为修改状态:
if (updateFeatureDisas.Any()) { // 修正原条件逻辑错误:用Any匹配存在相同ID的项,而非All var sqUpdateFeatureDisa = insertTagDisa.Where(a => updateFeatureDisas.Any(d => d.Id.Equals(a.Id, StringComparison.Ordinal))).ToList(); foreach (var trackedEntity in updateFeatureDisas) { var updateModel = sqUpdateFeatureDisa.First(u => u.Id == trackedEntity.Id); // 将新值映射到已跟踪的实体 _mapper.Map(updateModel, trackedEntity); // 无需调用Update,EF自动跟踪变更 } // 后续执行SaveChangesAsync即可保存更新 }
方案2:解除旧实体跟踪后更新新实例
如果必须使用AutoMapper生成的新实例,先解除旧实体的跟踪状态,再添加新实例的跟踪:
if (updateFeatureDisas.Any()) { var sqUpdateFeatureDisa = insertTagDisa.Where(a => updateFeatureDisas.Any(d => d.Id.Equals(a.Id, StringComparison.Ordinal))).ToList(); var updateFeatureDisa = _mapper.Map<List<FeatureDisa>>(sqUpdateFeatureDisa); foreach (var newEntity in updateFeatureDisa) { var oldEntity = updateFeatureDisas.First(e => e.Id == newEntity.Id); // 解除旧实体的跟踪状态 _featureDisaRepository.DbContext.Entry(oldEntity).State = EntityState.Detached; // 现在可以安全更新新实例 _featureDisaRepository.DbContext.Update(newEntity); } }
额外建议
你的仓储方法UpdateWithOutSaveChangeAsync是同步方法但命名带Async,容易造成误解,建议改名为UpdateRangeWithoutSaveChanges以符合命名规范。
内容的提问来源于stack exchange,提问作者Masoud H
相关产品推荐
相关产品推荐

