EF Core 6实体跟踪冲突求助:调用UpdateAsync触发异常
问题描述
我使用ASP.NET Core 6、EF Core 6结合Repository/UnitOfWork模式开发项目,调用仓储的UpdateAsync方法时抛出异常:
"The instance of entity type 'AppCase' cannot be tracked because another instance with the same key value for {} is already being tracked"
已知问题大致成因,尝试过AsNoTracking()但未解决,寻求可行方案。
源代码
MicroOrderBusiness.cs
public async Task<dynamic> CreateOrder(MicroOrderRequest requsetObject, string caseNo) { string processCode = "CEC1D433-C376-498B-8A80-B23DC3772024"; var caseModel = await _appCaseBusiness.PrepareAppCase(processCode, caseNo); if (caseModel == null) throw new Exception("Create request failed!"); var appCaseEntity = await _appCaseBusiness.AddNewCase(caseModel); // 通过Repository和UnitOfWork新增Case if (appCaseEntity == null) throw new Exception("Adding failed!"); var taskResponse = await _processTaskBusiness.ExecuteTask<MicroOrderRequest>(caseModel.FirstTaskCode, requsetObject); if (Equals(taskResponse, null)) throw new Exception("Response task is not found!"); caseModel.CaseObjects.Add(taskResponse); // 更新Case状态 appCaseEntity.State = STATE.DONE; appCaseEntity.CaseObject = JsonConvert.SerializeObject(appCase.CaseObjects); var updateCase = await _appCaseRepository.UpdateAsync(appCaseEntity); // 此处抛出异常 if (updateCase == null) throw new Exception($"Update state case failed!"); return caseModel; }
Program.cs
builder.Services.AddDbContext<AppDbContext>(options => options.UseOracle(connectionString, o => o.MigrationsAssembly(typeof(AppDbContext) .Assembly.FullName) .UseOracleSQLCompatibility("12"))); builder.Services.AddScoped(typeof(IUnitOfWork), typeof(AppDbContext));
解决方案
修正Repository的UpdateAsync方法逻辑
因为AddNewCase执行后,appCaseEntity已经被EF上下文跟踪,此时Update方法无需再附加实体,直接标记为修改状态即可:public async Task<AppCase> UpdateAsync(AppCase entity) { _context.Entry(entity).State = EntityState.Modified; await _context.SaveChangesAsync(); return entity; }避免在Update方法中执行
_context.Attach(entity),否则会触发同一Key的实体跟踪冲突。检查AddNewCase方法的返回值
确保AddNewCase返回的是EF上下文正在跟踪的实体实例,而非新建的未跟踪实例。若该方法内部使用了AsNoTracking(),会导致返回的实体未被跟踪,后续Update时上下文可能已因SaveChanges跟踪了新增的实体,引发冲突。处理上下文重复跟踪的情况
若业务场景中无法避免实体实例重复,可在Update方法中先检查本地跟踪缓存,存在则复制属性,不存在再附加:public async Task<AppCase> UpdateAsync(AppCase entity) { var trackedEntity = _context.Set<AppCase>().Local.FirstOrDefault(e => e.Id == entity.Id); if (trackedEntity != null) { // 将新属性值复制到已跟踪的实体上 _context.Entry(trackedEntity).CurrentValues.SetValues(entity); } else { _context.Attach(entity); _context.Entry(entity).State = EntityState.Modified; } await _context.SaveChangesAsync(); return trackedEntity ?? entity; }确保UnitOfWork上下文一致性
确认整个请求生命周期内使用的是同一个AppDbContext实例(因注册为Scoped,默认请求内单例),禁止在Repository或Business层手动创建新的上下文实例,否则会导致多上下文跟踪同一实体。
内容的提问来源于stack exchange,提问作者Đặng Đức

