使用Entity Framework 6添加含关联表的复杂模型记录时遇错误
模型定义
public class LvsBk { public int Id { get; set; } public string Species { get; set; } public virtual LvsSpecies LvsSpecies { get; set; } public int GridNo { get; set; } public virtual LvsGrid Grid { get; set; } } public class LvsSpecies { [Key] public string Species { get; set; } public string Description { get; set; } } public class LvsGrid { [Key] public int GridNo { get; set; } public string Species { get; set; } public virtual LvsSpecies Species { get; set; } // 注意:属性名与字段名重复,存在映射风险 }
问题场景与报错
场景1:直接赋值外键字段报错
执行以下代码时:
await _db.Lvsbk.AddAsync(new LvsBk { Site = request.LvsBkAddEdit.site, Species = request.LvsBkAddEdit.species, Grid = request.LvsBkAddEdit.GridNo }); await _db.SaveChangesAsync(canceallatioToken);
触发错误:
System.InvalidOperationException: 类型“System.Threading.ExecutionContext&”(属于类型“System.Runtime.CompilerServices.AsyncTaskMethodBuilder
1+AsyncStateMachineBox1[LvsModels.Dtos.ServiceResponse1[System.Collections.Generic.List1[LvsModels.CSVModels.LvsBkIMP]],LVSApi.Services.ImportService+d__3]”的“Context”属性)对于序列化或反序列化无效,因为它是指针类型、ref struct,或者包含未被具体类型替换的泛型参数。
场景2:赋值关联实体对象报错
改为直接关联实体对象时:
LvsSpecies = request.LvsBkAddEdit.LvsSpecies, Grid = request.LvsBkAddEdit.PriceGrid
触发错误:
System.AggregateException: 发生一个或多个错误。(实体类型“LvsSpecies”的实例无法被跟踪,因为已有一个具有键值“{Species: B}”的实例正在被跟踪。附加现有实体时,请确保每个给定键值只附加一个实体实例。)
已尝试操作
_db.Entry<LvsSpecies>(request.LvsBkAddEdit.LvsSpecies).State = EntityState.Detached; _db.Entry<PriceGrid>(request.LvsBkAddEdit.PriceGrid).State = EntityState.Detached;
说明:代码位于MediatR处理器中,request.LvsBkAddEdit是MediatR命令传入的对象,_db为EF Core数据上下文
解决方案
1. 修复场景1的序列化/赋值错误
第一个错误的核心是导航属性赋值类型不匹配:你给LvsBk的Grid(LvsGrid类型导航属性)直接赋了int类型的GridNo,导致EF处理异常。同时异步状态机序列化问题是因为请求对象可能包含了无法序列化的EF跟踪上下文。
修正代码:
await _db.Lvsbk.AddAsync(new LvsBk { Site = request.LvsBkAddEdit.site, Species = request.LvsBkAddEdit.species, GridNo = request.LvsBkAddEdit.GridNo // 赋值外键字段而非导航属性 }); await _db.SaveChangesAsync(canceallatioToken);
同时确保request.LvsBkAddEdit只包含可序列化的DTO字段,不要传递EF跟踪的实体对象。
2. 修复场景2的实体跟踪冲突
第二个错误是因为请求中的LvsSpecies实例已被当前DbContext跟踪,重复附加导致冲突。有两种解决方式:
方式一:复用DbContext中已跟踪的实体
通过Find方法获取DbContext中已存在的实体,避免重复附加:
// 处理LvsSpecies var species = await _db.LvsSpecies.FindAsync(request.LvsBkAddEdit.LvsSpecies.Species); if (species == null) { species = request.LvsBkAddEdit.LvsSpecies; _db.LvsSpecies.Add(species); } // 处理LvsGrid var grid = await _db.LvsGrid.FindAsync(request.LvsBkAddEdit.GridNo); if (grid == null) { grid = request.LvsBkAddEdit.Grid; _db.LvsGrid.Add(grid); } // 创建并保存LvsBk await _db.Lvsbk.AddAsync(new LvsBk { Site = request.LvsBkAddEdit.site, Species = request.LvsBkAddEdit.species, LvsSpecies = species, GridNo = request.LvsBkAddEdit.GridNo, Grid = grid }); await _db.SaveChangesAsync(canceallatioToken);
方式二:使用无跟踪实体
如果请求中的实体来自其他DbContext,在查询时添加AsNoTracking标记,确保不会被跟踪;如果是前端传入的DTO,直接映射为新的实体对象即可,不要传递EF实体。
额外优化:修复模型命名冲突
LvsGrid类中导航属性名与字段名均为Species,会导致EF映射异常,建议修改导航属性名:
public class LvsGrid { [Key] public int GridNo { get; set; } public string Species { get; set; } public virtual LvsSpecies LvsSpecies { get; set; } // 修改属性名避免冲突 }
内容的提问来源于stack exchange,提问作者Garth Dimond

