EF Core 8添加子实体时触发DbUpdateConcurrencyException问题排查
DbUpdateConcurrencyException 问题分析与解决方案
问题根源
从日志中的SQL语句可以定位核心问题:
- EF Core 针对新创建的
Device生成了UPDATE语句(而非预期的INSERT),这条UPDATE找不到对应数据库行(Device尚未插入),直接触发并发异常。 - 同时生成了多余的
OrganisationUPDATE语句,但实际上Organisation的属性并未发生任何修改。
导致该问题的关键原因是调用了_organisationRepository.Update(organisation):
public void Update(Organisation organisation) { _context.Organisations.Update(organisation); }
DbSet.Update()方法会强制将传入实体及其所有关联实体(包括刚添加的Device)的状态标记为Modified,而非Added。EF基于这个错误状态生成UPDATE语句,自然找不到目标行,抛出并发异常。
另外,查询Organisation时未加载Devices导航属性,虽然不是直接诱因,但会导致内存中Devices集合与数据库不一致,可能引发重复添加设备的逻辑错误。
解决方案
1. 移除不必要的Update调用
EF Core的变更追踪机制会自动检测实体状态变化,包括新添加的关联实体。无需手动调用Update,直接执行SaveChangesAsync即可:
修改Handle方法:
public async Task<Result> Handle(CreateDeviceCommand command, CancellationToken cancellationToken) { var organisation = await _organisationRepository.GetAsync(command.OrganisationId); if (organisation == null) return Result.Failure(CreateDeviceErrors.NullOrganisation); var deviceResult = Device.Create(organisation, command.DeviceId, command.DeviceName); if (deviceResult.IsFailure) return Result.Failure(deviceResult.Error); var addDeviceResult = organisation.AddDevice(deviceResult.Value); if (addDeviceResult.IsFailure) return Result.Failure(addDeviceResult.Error); // 移除该行,不需要手动触发Update // _organisationRepository.Update(organisation); await _unitOfWork.SaveChangesAsync(cancellationToken); return Result.Success(); }
2. 优化Organisation查询(可选)
获取Organisation时显式加载Devices导航属性,确保内存集合与数据库一致,避免重复添加设备的逻辑出错:
修改OrganisationRepository的GetAsync方法:
public async Task<Organisation> GetAsync(Guid organisationId) { return await _context.Organisations .Include(o => o.Devices) // 显式加载导航属性 .FirstOrDefaultAsync(o => o.Id == organisationId); }
3. 调整Update逻辑(特殊场景适配)
如果某些业务场景必须保留Repository的Update方法,需修改逻辑,仅更新Organisation自身,不覆盖关联实体状态:
public void Update(Organisation organisation) { // 仅标记Organisation自身为Modified,不处理关联实体 _context.Entry(organisation).State = EntityState.Modified; }
注意:当前场景下Organisation无属性变更,仍无需调用该方法。
验证效果
修改后,EF Core会正确将新添加的Device标记为Added,生成INSERT语句,同时不会生成多余的Organisation UPDATE语句,彻底避免并发异常。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

