EF Core中外键表行的插入与更新及并发异常问题排查
EF Core中外键表行插入与更新的并发异常解决方法
问题分析
执行更新关联表现有记录+插入新记录的操作时,调用_unitOfWork.Complete()抛出乐观并发异常,核心原因是EF Core尝试更新的记录未被上下文正确跟踪,或者记录已被其他操作修改。
解决方案
1. 确保现有实体被上下文正确跟踪
如果agentDetail.AgentGDS_AccessDetails是离线实体(比如从DTO转换而来、未通过当前上下文查询加载),需要手动附加到上下文并标记修改状态:
foreach (var accessDetail in agentDetail.AgentGDS_AccessDetails) { // 将实体附加到当前上下文 _context.Attach(accessDetail); // 明确标记需要更新的字段 _context.Entry(accessDetail).Property(x => x.Deleted).IsModified = true; }
如果是通过当前上下文加载的实体,确保加载时没有使用AsNoTracking(),否则上下文不会跟踪实体的变更。
2. 配置并使用并发令牌(RowVersion)
在AgentGDS_AccessDetail实体中添加并发令牌字段,让EF Core自动处理并发检查:
public class AgentGDS_AccessDetail { // 原有字段... [Timestamp] public byte[] RowVersion { get; set; } }
同时在DbContext的配置中声明该字段为行版本:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<AgentGDS_AccessDetail>() .Property(x => x.RowVersion) .IsRowVersion(); }
配置后,EF Core会在更新时自动对比行版本,若记录已被其他进程修改则抛出异常,此时可捕获异常并重新加载数据重试。
3. 捕获并发异常并处理冲突
捕获DbUpdateConcurrencyException,重新加载数据库最新数据后合并更改重试:
try { _unitOfWork.Complete(); } catch (DbUpdateConcurrencyException ex) { foreach (var entry in ex.Entries) { var databaseValues = entry.GetDatabaseValues(); if (databaseValues == null) { // 记录已被删除,执行对应业务逻辑(如提示用户) continue; } // 合并客户端与数据库的变更,可根据业务需求调整优先级 entry.OriginalValues.SetValues(databaseValues); entry.CurrentValues.SetValues(entry.CurrentValues); } // 重试保存操作 _unitOfWork.Complete(); }
4. 提前验证记录存在性
在更新前检查要修改的记录是否仍存在于数据库中:
var targetIds = agentDetail.AgentGDS_AccessDetails.Select(x => x.GDS_AccessId).ToList(); var existingRecords = _context.AgentGDS_AccessDetails .Where(x => targetIds.Contains(x.GDS_AccessId)) .ToList(); if (existingRecords.Count != targetIds.Count) { // 部分记录已被删除,执行对应处理逻辑 }
内容的提问来源于stack exchange,提问作者Ansar Farook
相关产品推荐
相关产品推荐

