使用EF更新租车记录时触发DbUpdateConcurrencyException如何解决
排查与解决方法
排查步骤
- 第一步:打印EF Core生成的实际SQL语句,确认
SaveChanges执行的UPDATE语句的WHERE条件是否符合预期,是否存在匹配多条记录的可能。可以通过在上下文配置中添加日志输出实现:protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.LogTo(Console.WriteLine); // 输出SQL到控制台 } - 第二步:直接在数据库执行SQL查询
SELECT COUNT(*) FROM CarRentalFields WHERE CarNumber = 1000 AND ActualReturnDate IS NULL,确认符合条件的记录确实只有1条,排除事务未提交、脏读等数据层面问题。 - 第三步:检查对应数据库表是否存在UPDATE触发器,触发器执行的额外操作可能会增加总影响行数。
- 第四步:核对
modelBuilder中CarRentalFields实体的配置,检查主键、并发令牌的配置是否和数据库表结构一致。
对应解决方案
场景1:主键配置错误
如果实体主键被误配置为非唯一字段(如CarNumber),会导致UPDATE语句的WHERE条件按非唯一字段匹配,可能命中多条记录。需修改为和数据库一致的主键配置,例:
// 假设表实际主键为自增Id字段 modelBuilder.Entity<CarRentalFields>().HasKey(x => x.Id);
场景2:存在UPDATE触发器
如果表上有不可移除的UPDATE触发器,EF Core 7及以上版本可以通过配置触发器避免EF校验影响行数时报错:
modelBuilder.Entity<CarRentalFields>() .ToTable(t => t.HasTrigger("触发器实际名称"));
场景3:上下文跟踪异常
同上下文下重复跟踪了多个相同实体实例时,也可能出现该错误,可通过修改查询和更新逻辑解决:
public void updateStatus(int carNumber1, string actualDate1) { DateTime accReturn = DateTime.Parse(actualDate1); // 加空判断避免空引用异常 var orderCar1 = db.CarRentalFields .FirstOrDefault(carNum => carNum.CarNumber == carNumber1 && carNum.ActualReturnDate == null); if (orderCar1 == null) { // 可根据业务逻辑添加不存在的处理 return; } // 手动清理多余跟踪 db.ChangeTracker.Clear(); orderCar1.ActualReturnDate = accReturn; db.Update(orderCar1); db.SaveChanges(); }
场景4:误配置并发令牌
如果非必要字段被配置为并发令牌,会导致UPDATE的WHERE条件添加多余匹配项,命中多条记录,移除多余的IsConcurrencyToken()配置即可。
内容的提问来源于stack exchange,提问作者elirans
相关产品推荐
相关产品推荐

