EF Core 3.0 Code First循环操作集合触发空引用异常求助
首先,你的猜测方向是对的,但具体触发空引用的原因其实是EF Core上下文的实体跟踪机制和你查询时的导航属性访问逻辑导致的:
当你第一次循环添加Item后,这个新实体已经被EF上下文跟踪了(即使还没调用SaveChanges)。第二次迭代执行FirstOrDefault(a => a.Profile.Id == profileCount.Key)时,EF会把上下文里已跟踪的所有Item(包括刚添加的那个)都纳入查询范围。而你添加的Item只设置了ProfileId外键,并没有赋值Profile导航属性,此时访问a.Profile.Id就会因为Profile为null而抛出NullReferenceException。
解决方案
这里有几个可行的修复方案,按推荐程度排序:
1. 直接使用外键字段查询(最稳妥)
把查询条件从导航属性改为外键字段,完全避免访问null的导航属性,同时查询效率更高:
var oldItem = dc.Stock.Items.FirstOrDefault(a => a.ProfileId == profileCount.Key);
这个方案不需要修改其他逻辑,直接解决空引用问题,是最直接的修复方式。
2. 查询时排除未保存的实体
如果一定要用导航属性查询,可以让EF只查询数据库中已存在的实体,忽略上下文里刚添加但未保存的实体:
var oldItem = dc.Stock.Items.AsNoTracking().FirstOrDefault(a => a.Profile.Id == profileCount.Key);
AsNoTracking()会让EF跳过上下文的跟踪缓存,直接查询数据库。注意:这个方法返回的实体不会被上下文跟踪,后续如果要修改需要额外处理。
3. 添加实体时关联Profile导航属性
如果业务上需要Profile导航属性在添加后就可用,可以在添加前先查询对应的Profile实体并关联:
var profile = dc.Profiles.Find(profileCount.Key); if (profile != null) { dc.Stock.Items.Add(new Item { Price = 0, Profile = profile, Quantity = profileCount.Value, WasChanged = false }); }
这样添加的Item的Profile导航属性就不会为null,后续查询访问a.Profile.Id也不会出错。不过这个方案会多一次数据库查询,适合Profile数据经常被用到的场景。
最后提个小提醒:循环结束后记得调用dc.SaveChanges(),不然所有添加的实体都不会持久化到数据库哦。
内容的提问来源于stack exchange,提问作者Jamil

