使用Entity Framework Core更新DbContext时遇并发异常的问题排查
问题分析与解决方案
错误原因
DbUpdateConcurrencyException 触发的核心原因是:EF Core 执行更新操作时,预期会修改1行数据,但实际数据库中没有匹配到对应行,或者该行已被其他操作修改/删除。结合你的代码,主要问题出在实体跟踪与更新逻辑的错误使用上。
代码中的具体问题
1. Update 方法的逻辑错误
// 注:原代码返回值写了book,实际应为bool,属于语法错误 public bool Update(todoList model) { // 新建一个脱离上下文的todoListDataModel对象 var data = new todoListDataModel { seqNo = model.seqNo, customerId = model.customerId, ... }; // 直接对脱离上下文的对象调用Update,EF会将其标记为Modified状态 _context.TODOLIST.Update(data); try { // 多余操作:查询数据但未使用,还可能干扰上下文跟踪 var todoList = _context.TODOLIST.FirstOrDefault(u => u.seqNo == model.seqNo); _context.SaveChanges(); return true; } catch (SystemException ex) { throw ex; } }
- 你新建的
data是未被DbContext跟踪的全新对象,调用Update后,EF会直接生成更新语句。但如果数据库中不存在seqNo匹配的记录,或该记录的并发令牌(如时间戳)与data中的值不匹配,就会触发并发异常。 - 方法内的
FirstOrDefault查询完全冗余,反而可能导致上下文跟踪的实体与手动创建的data冲突。
2. updateCompliteFlg 方法的冗余操作
public string updateCompliteFlg(string operatorId , string id) { try { // 通过FindById获取的model是被DbContext跟踪的实体 var model = _repository.FindById(id); model.resultFlg = "1"; model.operatorId = operatorId; ... // 多余步骤:将已跟踪实体转成新的data对象 var data = updateCompliteFlgToData(model, operatorId, id); // 调用错误的Update方法更新脱离上下文的data对象 if (_repository.Update(data)) { return "Update!"; } else { return "Failed!"; } } catch { return "Exception!"; } }
- 你已经获取了被上下文跟踪的实体
model,直接修改属性后EF会自动跟踪变更,不需要转成新的data再调用Update。这种冗余操作会导致使用脱离上下文的实体更新,触发异常。
修正后的代码示例
方案1:使用已跟踪实体更新(推荐)
先查询获取被上下文跟踪的实体,修改属性后直接保存:
// 修正后的Update方法 public bool Update(todoList model) { try { // 查询要更新的实体,确保被上下文跟踪 var existingData = _context.TODOLIST.FirstOrDefault(u => u.seqNo == model.seqNo); if (existingData == null) { return false; // 数据库无对应记录 } // 赋值更新属性 existingData.customerId = model.customerId; // 其他属性更新... _context.SaveChanges(); return true; } catch (DbUpdateConcurrencyException ex) { throw ex; // 捕获并发异常,可自定义处理逻辑 } } // 修正后的updateCompliteFlg方法 public string updateCompliteFlg(string operatorId , string id) { try { var model = _repository.FindById(id); if (model == null) { return "Failed!"; } model.resultFlg = "1"; model.operatorId = operatorId; // 其他属性修改... // 直接保存上下文变更,无需额外操作 _context.SaveChanges(); return "Update!"; } catch (DbUpdateConcurrencyException) { return "Data has been modified by others!"; } catch { return "Exception!"; } }
方案2:使用脱离上下文的实体更新(适合分布式场景)
若必须使用脱离上下文的对象更新,需确保主键正确,手动标记实体状态:
public bool Update(todoList model) { try { var data = new todoListDataModel { seqNo = model.seqNo, customerId = model.customerId, // 若有并发令牌(如Timestamp),需携带最新值 // Timestamp = model.Timestamp, ... }; // 附加实体到上下文并标记为Modified _context.TODOLIST.Attach(data); _context.Entry(data).State = EntityState.Modified; // 可选:仅更新部分属性 // _context.Entry(data).Property(u => u.customerId).IsModified = true; _context.SaveChanges(); return true; } catch (DbUpdateConcurrencyException ex) { throw ex; } }
额外注意事项
- 检查数据库中是否存在
seqNo/id对应的记录,不存在也会触发该异常。 - 若实体配置了并发令牌(如
[Timestamp]属性),需确保更新时携带最新令牌值,否则会触发并发校验失败。 - 避免在同一上下文操作中同时使用已跟踪实体和手动创建的脱离上下文实体,易导致跟踪冲突。
内容的提问来源于stack exchange,提问作者tatsuya
相关产品推荐
相关产品推荐

