加载多对多关联实体时同时插入引发的瞬态NRE问题
问题描述
现有三个表A、B、AtoB,A与B通过AtoB表建立多对多关联,实体类定义如下:
public class A { public int Id {get;set;} public List<B> Bs {get;set;} public List<AtoB> AtoBs {get;set;} } public class B { public int Id {get;set;} public List<A> As {get;set;} public List<AtoB> AtoBs {get;set;} } public class AtoB { public int AId {get;set;} public int BId {get;set;} public A Aa {get;set;} public B Bb {get;set;} }
DbContext定义:
public class MyContext : DbContext { public DbSet<A> As {get;set;} public DbSet<B> Bs {get;set;} public DbSet<AtoB> AtoBs {get;set;} }
EF中对A的配置如下:
public void Configure(EntityTypeBuilder<A> builder) { builder.HasMany(x => x.Bs) .WithMany(x => x.As) .UsingEntity<AtoB>( x => x.HasOne(x => x.B) .WithMany(x => x.AtoBs) .HasForeignKey(x => x.BId) .OnDelete(DeleteBehavior.Restrict), x => x.HasOne(x => x.A) .WithMany(x => x.AtoBs) .HasForeignKey(x => x.AId) .OnDelete(DeleteBehavior.Restrict) ); }
通过以下方式加载数据:
var as = await _dbContext.As .Include(x => x.Bs) .ThenInclude(x => x.AtoBs) .ToListAsync();
当实体加载完成且列表非空时,执行以下查询并访问BId属性时会抛出NRE异常:“Object reference not set to an instance of an object”:
var filteredAs = as.Select(a => new { projected = a.AtoBs.FirstOrDefault().BId }).ToList();
疑惑点:该异常似乎在向表中插入实体的同时出现,但如果AtoB行尚未插入,那么A中应该不存在Bs才对?
问题原因分析
导航属性加载范围不匹配
你使用的查询Include(x => x.Bs).ThenInclude(x => x.AtoBs),仅会加载每个B实体关联的AtoBs集合,并不会加载A实体自身的AtoBs导航属性。查询返回的A对象中,AtoBs可能为null或空列表。此时调用a.AtoBs.FirstOrDefault()会返回null,继续访问BId自然触发空引用异常。EF内存实体状态与数据库持久化的差异
在插入实体的场景中,EF上下文会在内存中维护实体关联:当你将B实例添加到A的Bs集合时,即便未执行SaveChanges()将AtoB记录写入数据库,EF已在内存中建立A与B的关联,此时A的Bs集合会包含对应的B实例。但A的AtoBs集合并不会自动被填充——因为Bs和AtoBs是两个独立的导航属性,EF不会因加载或关联Bs就自动初始化或填充AtoBs。这就导致即便Bs非空,AtoBs依然可能为null或空,进而引发NRE。
内容的提问来源于stack exchange,提问作者Alexander
相关产品推荐
相关产品推荐

