EF Core 直到直接访问时才返回 null 关系

44

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

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);

现在当我请求突变体时,OriginalCode为空:

Null OriginalCode

但是只要我像下面这样请求OriginalCode

OriginalCodes

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

Filled Object

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

2个回答

78

原因在 EF Core 文档的 加载相关数据 部分有解释。

第一个问题是因为 EF Core 目前不支持延迟加载,因此通常情况下,除非你通过急切或显式加载来加载它们,否则会得到导航属性的 null 值。然而,在 急切加载 部分中包含以下内容:

提示
Entity Framework Core 将自动修复与上下文实例之前加载到的任何其他实体的导航属性。因此,即使您没有明确包含导航属性的数据,如果某些或所有相关实体之前已加载,则仍可能填充该属性。

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

现在,我不确定你想要解决哪个问题,所以将尝试解决两个问题。

可以使用目前可用的一种方法(例如急切加载)来解决第一个问题:

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

第二个行为是“按设计”而无法控制。如果您想避免它,请确保使用全新的DbContext实例只执行单个查询以检索所需数据,或者使用无跟踪查询

更新:从v2.1开始,EF Core支持延迟加载。但是它不是默认启用的,因此要使用它,应该将所有导航属性标记为virtual,安装Microsoft.EntityFrameworkCore.Proxies并通过UseLazyLoadingProxies调用启用它,或者利用无代理的延迟加载 - 这两种方法都在EF Core文档中有示例说明。


2
正如你所猜测的,我想控制第一个行为。但仍有一个重要问题。按照你提到的方式,我应该明确地指定要填充的关系,对吗? - ConductedClever
2
确实。您必须使用多个Include / ThenInclude方法指定要“包含”的每个内容。据我所知,未来有一些计划可以自动完成此操作,但目前这是唯一的选择。 - Ivan Stoev
猜测呢,谁也不知道 - 例如在EF Core标签中,在你的问题之前看到了一些问题 - 我能阻止Entity Framework Core使用部分数据填充我的结果吗? :) - Ivan Stoev
2
现在 EF Core 支持延迟加载: https://learn.microsoft.com/en-us/ef/core/querying/related-data#lazy-loading - Pejman Nikram
2
尽管我认为没有设置为默认选项意味着它不是推荐的解决方案!我认为,如果正确使用,非惰性方法会更难编写但在运行时更好,这将将许多未来数据库请求放到一个请求中。 - ConductedClever
显示剩余2条评论

3

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

install-package Microsoft.EntityFrameworkCore.Proxies

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

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

        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接