Entity Framework Code First多表添加时ObjectContext不同报错如何解决?
问题原因与解决方案
你遇到的报错翻译为:无法定义两个对象之间的关系,因为它们附加到了不同的ObjectContext对象,存在重复对象。
错误根因
你的UnitOfWork(工作单元)实现不符合规范,不同Repository使用的是独立初始化的DbContext实例,从ViolationTypeRepository查询出的ViolationType实体归该仓库的上下文管理,InspectionItem实体归InspectionItemRepository的上下文管理,两个不同上下文的实体建立关联时就会触发该错误。
现有代码存在的问题
- 上下文未共享:每个Repository内部单独实例化DbContext,没有通过UnitOfWork统一管理上下文实例
- 提交逻辑错误:通用Create方法内部直接调用了
SaveChanges,违背了UnitOfWork所有操作完成后统一提交的设计原则,不仅性能低,也无法保证事务一致性 - 关联赋值冗余:关联对象时查询完整实体赋值,完全可以直接赋值外键字段规避上下文冲突,同时减少不必要的数据库查询
修复步骤
第一步:修复UnitOfWork上下文共享逻辑
确保UnitOfWork初始化时仅创建一个DbContext实例,所有Repository都复用该实例:
public class UnitOfWork : IUnitOfWork { // 替换为你自己的DbContext类名 private readonly YourDbContext _context; public IInspectionItemRepository InspectionItemRepository { get; } public IViolationTypeRepository ViolationTypeRepository { get; } public IInspectionItemViolationCategoryRepository InspectionItemViolationCategoryRepository { get; } public UnitOfWork(YourDbContext context) { _context = context; // 所有Repository都传入同一个Context实例 InspectionItemRepository = new InspectionItemRepository(_context); ViolationTypeRepository = new ViolationTypeRepository(_context); InspectionItemViolationCategoryRepository = new InspectionItemViolationCategoryRepository(_context); } // 统一提交入口 public int SaveChanges() { try { return _context.SaveChanges(); } catch (Exception ex) { LogHandler.LogInfo(2501, ex.Message); throw; } } // 上下文释放逻辑可按需补充 }
第二步:修改通用Create方法,移除内部提交逻辑
public T Create(T item) { try { if (ufb != null && ufb.CurrentUser != null) { SetValue("CreatedByUserId", item, ufb.CurrentUser.Id); SetValue("UpdatedByUserId", item, ufb.CurrentUser.Id); } SetValue("DateCreated", item, DateTime.Now); SetValue("DateUpdated", item, DateTime.Now); var newEntry = this.DbSet.Add(item); this.Context.Database.Log = message => LogHandler.LogInfo(1111, message); BuildMetaData(item, true, true); return newEntry; } catch (DbEntityValidationException dbEx) { string msg = string.Empty; foreach (var validationErrors in dbEx.EntityValidationErrors) { foreach (var validationError in validationErrors.ValidationErrors) { msg += $"{validationError.PropertyName}---{validationError.ErrorMessage}||"; } } throw new Exception("7777 CREATE EntityValidationErrors: " + msg); } }
第三步:优化业务层创建逻辑,统一提交
public InspectionItem Create(InspectionItem inspectionItem) { try { List<int> inspectionCategoryIdlist = inspectionItem.InspectionItemCategory .Select(c => c.ViolationTypeId) .ToList(); inspectionItem.InspectionItemCategory.Clear(); inspectionItem.InspectionItemNumber = CalculateInspectionItemNumber(inspectionItem.InspectionItemId); // 添加主实体 UnitOfWork.InspectionItemRepository.Create(inspectionItem); // 检测变化获取自增主键(如果是自增ID需要加这行) UnitOfWork.Context.ChangeTracker.DetectChanges(); if (inspectionItem.InspectionItemId > 0) { foreach (var violationTypeId in inspectionCategoryIdlist) { var relationEntity = new InspectionItemViolationCategory { // 直接赋值外键,无需查询关联实体 InspectionItemId = inspectionItem.InspectionItemId, ViolationTypeId = violationTypeId }; UnitOfWork.InspectionItemViolationCategoryRepository.Create(relationEntity); } } // 所有操作完成后统一提交,自动保证事务一致性 UnitOfWork.SaveChanges(); return inspectionItem; } catch (Exception ex) { LogHandler.LogError(2101, "Commit Fail in Inspection Item Create", ex); throw; } }
额外优化建议
如果你的项目用了依赖注入,直接把DbContext注册为Scoped生命周期,就能自动保证同一个请求内所有Repository拿到的是同一个上下文实例,无需手动在UnitOfWork里管理。
内容的提问来源于stack exchange,提问作者AbdulAleem
相关产品推荐
相关产品推荐

