EF Core returns 空关系,直到直接访问

EF Core returns null relations until direct access

我有一些像下面这样的模型:

public class Mutant
{
    public long Id { get; set; }
    ...

    // Relations
    public long OriginalCodeId { get; set; }
    public virtual OriginalCode OriginalCode { get; set; }
    public int DifficultyLevelId { get; set; }
    public virtual DifficultyLevel DifficultyLevel { get; set; }
}

public class OriginalCode
{
    public long Id { get; set; }
    ...

    // Relations
    public virtual List<Mutant> Mutants { get; set; }
    public virtual List<OriginalCodeInputParameter> OriginalCodeInputParameters { get; set; }
}

DBContextOnModelCreating 中,我建立了如下关系:

        modelBuilder.Entity<Mutant>()
            .HasOne(m => m.OriginalCode)
            .WithMany(oc => oc.Mutants)
            .HasForeignKey(m => m.OriginalCodeId)
            .OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);

        modelBuilder.Entity<Mutant>()
            .HasOne(m => m.DifficultyLevel)
            .WithMany(dl => dl.Mutants)
            .HasForeignKey(m => m.DifficultyLevelId)
            .OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);

现在当我请求 Mutants 时,OriginalCode 为空:

但是一旦我请求 OriginalCode 如下:

那么突变体的OriginalCode字段将不为空:

这是什么原因,我该如何解决?

原因在 EF Core 文档的 Loading Related Data 部分进行了解释。

第一个行为是因为 EF Core 当前不支持延迟加载,因此通常您会获得 null 导航属性,直到您通过预先加载或显式加载专门加载它们。但是,预加载 部分包含以下内容:

Tip
Entity Framework Core will automatically fix-up navigation properties to any other entities that were previously loaded into the context instance. So even if you don't explicitly include the data for a navigation property, the property may still be populated if some or all of the related entities were previously loaded.

这解释了为什么在第二种情况下导航 属性 不为空。

现在,我不确定您想修复这两种行为中的哪一种,所以会尝试解决这两种行为。

第一个行为可以是 "fixed" 通过使用当前可用的方法之一来加载相关数据,例如预先加载:

var mutants = db.Mutants.Include(m => m.OriginalCode).ToList();

第二种行为是"by design",无法控制。如果你想避免它,请确保使用全新的 DbContext 实例来执行单个查询以重试所需的数据。

更新: 从 v2.1 开始,EF Core 支持 Lazy Loading. However it's not enabled by default, so in order to utilize it one should mark all navigation properties virtual, install Microsoft.EntityFrameworkCore.Proxies and enable it via UseLazyLoadingProxies call, or utilize Lazy-loading without proxies - 两者都在 EF Core 文档中用示例进行了解释。

使用包管理器控制台安装Microsoft.EntityFrameworkCore.Proxies

install-package Microsoft.EntityFrameworkCore.Proxies

然后在您的上下文中 class 添加 .UseLazyLoadingProxies():

namespace SomeAPI.EFModels
{
    public partial class SomeContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder
                    .UseLazyLoadingProxies()
                    .UseSqlServer(connectionString);
            }

        }
    }
}