UOW模式更新报错:同ID Person实体已被另一实例跟踪如何解决
报错根因
你通过GetByID查询到的Person实例已经被当前请求的DbContext跟踪,此时直接传入游离态的model.person实例调用Update、赋值给car.owner,会触发同主键多实例的跟踪冲突。
修复方案
方案1:复用已跟踪的Person实例(推荐)
不要直接操作请求传入的model.person实例,将需要更新的字段同步到已被跟踪的实例上,关联时也复用该跟踪实例即可。同时已跟踪的实体修改属性后,EF会自动识别变更,无需手动调用Update方法。
修正后的代码如下:
public async Task<bool> addUpdateCar(AddUpdateCarModel model) { var car = await unitOfWork.CarRepository.GetByID(model.car_id); var trackedPerson = await unitOfWork.PersonRepository.GetByID(model.person.id); Person relatedPerson; if (trackedPerson == null) { // 新增场景直接使用传入的Person实例 relatedPerson = model.person; unitOfWork.PersonRepository.Insert(relatedPerson); } else { // 更新场景将变更同步到已跟踪的实例,无需替换实例 trackedPerson.name = model.person.name; // 其他需要更新的Person字段均在此处赋值 relatedPerson = trackedPerson; } // 关联同一个Person实例,避免跟踪冲突 car.owner = relatedPerson; unitOfWork.CarRepository.Update(car); unitOfWork.Save(); return true; }
如果Person字段较多,可借助AutoMapper等映射工具直接将model.person的属性映射到trackedPerson,减少手动赋值代码。
方案2:查询时关闭实体跟踪
如果不需要对查询到的Person做后续跟踪操作,可以修改仓储的GetByID方法,查询时添加AsNoTracking(),让查询返回的实例不被上下文跟踪,就不会和model.person产生冲突:
public async Task<Person> GetByID(int id) { return await context.person.AsNoTracking().FirstOrDefaultAsync(p => p.id == id); }
该方案适合只读查询场景,UOW模式下更推荐方案1保证实体状态统一。
内容的提问来源于stack exchange,提问作者Limitless
相关产品推荐
相关产品推荐

