使用泛型仓储与工作单元模式时UpdateAsync数据库更新异常排查
问题:泛型仓储+工作单元模式下UpdateAsync报错「预期影响1行但未达到」
我的应用采用Generic Repository(泛型仓储)与UnitOfWork(工作单元)模式,调用UpdateFixtureAsync方法时出现错误:The database operation was expected to affect 1 row(s), on UpdateAsync。
出错方法代码
public async Task<bool> UpdateFixtureAsync(Fixture fixtureItem) { var fixtureRepository = _unitOfWork.GetRepository<Fixture>(); await fixtureRepository.UpdateAsync(fixtureItem); await _unitOfWork.CommitAsync(); return true; }
调用链核心逻辑
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { for (int i = 0; i < 7; i++) { await AddFixturesAsync(); await Task.Delay(1000, stoppingToken); } } } private async Task ProcessEventAsync(Location location) { foreach (var ev in location.Events) { var fixture = await _fixtureServices.GetFixtureAsync(ev); if (fixture == null) { fixture = new Fixture { name=ev.Name }; await _fixtureServices.AddFixtureAsync(fixture); } await ProcessFixtureAsync(fixture); } } private async Task ProcessFixtureAsync(Fixture fixture) { using (var client = new HttpClient()) { // 省略HTTP请求与反序列化逻辑 foreach (var location in root.Locations) { fixture.Position= location.Position; await _fixtureServices.UpdateFixtureAsync(fixture); } } }
排查思路
- 实体跟踪异常:检查
Fixture是否被当前DbContext正确跟踪。比如新增实体后立即进入更新流程,或多上下文实例导致实体脱离跟踪。 - 重复更新冲突:
ProcessFixtureAsync中同一fixture被循环调用多次UpdateFixtureAsync,第一次提交后实体状态变更,后续更新可能无法被上下文识别。 - 并发更新冲突:异步循环中可能存在多线程同时更新同一条数据,导致某一次更新时数据已被修改,影响行数为0。
- 仓储实现缺陷:检查泛型仓储的
UpdateAsync是否正确设置实体状态(如未调用DbContext.Update或未处理Detached状态)。
解决方案
1. 避免同一实体重复更新
修改ProcessFixtureAsync逻辑,批量更新属性后统一提交一次,而非循环内多次更新:
private async Task ProcessFixtureAsync(Fixture fixture) { using (var client = new HttpClient()) { // 省略HTTP请求与反序列化逻辑 if(root.Locations.Any()) { // 按业务需求选择最终要保留的Position(示例取最后一个) fixture.Position = root.Locations.Last().Position; await _fixtureServices.UpdateFixtureAsync(fixture); } } }
若业务确实需要多次更新,每次更新后需重新从上下文获取最新实体,确保跟踪状态正确。
2. 修复实体跟踪逻辑
在泛型仓储的UpdateAsync中添加实体附加处理,确保脱离跟踪的实体能被上下文识别:
public async Task UpdateAsync(T entity) { var entry = _dbContext.Entry(entity); if(entry.State == EntityState.Detached) { _dbContext.Attach(entity); } entry.State = EntityState.Modified; await Task.CompletedTask; }
3. 处理并发冲突
给Fixture实体添加并发令牌,让EF自动检测并发冲突:
public class Fixture { // 其他属性 [Timestamp] public byte[] RowVersion { get; set; } }
在更新方法中捕获并发异常并处理:
public async Task<bool> UpdateFixtureAsync(Fixture fixtureItem) { var fixtureRepository = _unitOfWork.GetRepository<Fixture>(); try { await fixtureRepository.UpdateAsync(fixtureItem); await _unitOfWork.CommitAsync(); return true; } catch(DbUpdateConcurrencyException ex) { var entry = ex.Entries.Single(); var dbValues = entry.GetDatabaseValues(); if(dbValues == null) return false; // 实体已被删除 // 重置原始值为数据库最新值,重新提交 entry.OriginalValues.SetValues(dbValues); await _unitOfWork.CommitAsync(); return true; } }
4. 规范工作单元生命周期
确保工作单元在单次操作范围内是唯一实例,避免多上下文导致的跟踪混乱。比如在后台循环中,每次AddFixturesAsync操作使用独立的工作单元实例。
内容的提问来源于stack exchange,提问作者pakerinjo
相关产品推荐
相关产品推荐

