实体类如何两次引用另一表?导航属性为null问题求解
问题分析与解决方案
这个问题我碰到过好多次,本质是Entity Framework的关联数据加载策略导致的,咱们一步步说清楚:
为什么Unidade导航属性会是null?
你已经给UnidadeId设置了合法值,但EF默认采用延迟加载机制:只有当你第一次访问Unidade这个导航属性时,EF才会自动去数据库查询关联的Unidade数据。但如果这时候你的DbContext已经被释放(比如放在using块里,代码块结束后上下文就销毁了),EF就无法发起查询,导航属性自然会变成null。
另外,即使上下文没被释放,如果你没有明确告诉EF要加载关联数据,也可能因为延迟加载的触发时机问题导致暂时无法获取数据,但这种情况比较少见,核心还是上下文生命周期或者加载策略的问题。
解决方法
1. 预先加载(Eager Loading)—— 最推荐的方式
查询Estrutura的时候,直接用Include方法指定要一起加载的导航属性,EF会生成JOIN查询一次性把关联数据拉回来:
var estrutura = context.Estruturas .Include(e => e.Unidade) // 加载关联的Unidade数据 .Include(e => e.UnidadeCompra) // 如果需要用到这个,也一起加载 .Single(e => e.Id == 120898); // 此时访问Unidade.Descricao就不会是null了 var descricao = estrutura.Unidade.Descricao;
2. 显式加载(Explicit Loading)—— 适合已获取实体的场景
如果你已经拿到了Estrutura实例,且DbContext还处于可用状态,可以手动触发导航属性的加载:
var estrutura = context.Estruturas.Single(e => e.Id == 120898); // 手动加载Unidade关联数据 context.Entry(estrutura).Reference(e => e.Unidade).Load(); // 现在就能正常访问了 var descricao = estrutura.Unidade.Descricao;
3. 检查延迟加载是否启用
虽然你的导航属性已经加了virtual(这是EF延迟加载的必要条件),但如果DbContext被手动禁用了延迟加载,那就算有virtual也没用。可以检查你的上下文配置:
public class YourDbContext : DbContext { public YourDbContext(DbContextOptions<YourDbContext> options) : base(options) { // 如果有这行,说明禁用了延迟加载,注释掉或者改为true // this.ChangeTracker.LazyLoadingEnabled = false; } // 定义DbSet public DbSet<Estrutura> Estruturas { get; set; } public DbSet<Unidade> Unidades { get; set; } }
默认情况下LazyLoadingEnabled是true,除非你手动修改过。
最后补充一点:确认UnidadeId对应的记录确实存在于Unidades表中,虽然你说值是合法的,但偶尔也会出现外键值在关联表中不存在的情况,这种时候就算加载了,导航属性也会是null。
内容的提问来源于stack exchange,提问作者Sergio Di Fiore
相关产品推荐
相关产品推荐

