EF Core 5.0.4 - 从核心 3.1 升级后,通过 Include() 的急切加载不起作用

Posted

技术标签:

【中文标题】EF Core 5.0.4 - 从核心 3.1 升级后,通过 Include() 的急切加载不起作用【英文标题】:EF Core 5.0.4 - Eager Loading via Include() does not work after upgrade from core 3.1 【发布时间】:2021-06-28 17:29:49 【问题描述】:

我们最近从 3.1 迁移到 .net core 5.0.4,并且 EF 急切加载已停止运行。它不再加载依赖对象。

我们尝试像这样加载依赖实体(但未加载相关的 Permissions obj):

var test = _db.Guardians.Include(x => x.Permissions).First(x => x.Id == id);

虽然 PermissionId Fk 已填充,但依赖实体不是:

除了迁移到 EF Core 5 之外,没有其他变化。

POCO 以防万一:

public class GuardianModel : PersonModel

    public int PermissionsId  get; set; 
    [ForeignKey("PermissionsId")]
    public virtual GuardianPermission Permissions  get; set; 

    public GuardianModel() : base()
    
        Permissions = new GuardianPermission();
        Active = true;
    

public class GuardianPermission

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id  get; set; 
    public bool MessageAsEmail  get; set; 
    public bool Permission  get; set; 
    public bool Message  get; set; 
    public bool CovidReportEmail  get; set; 
    public bool PermissionEmails  get; set; 
    public bool System  get; set; 
    public GuardianPermission()
    
        MessageAsEmail = true;
        Permission = true;
        Message = true;
        PermissionEmails = true;
    

我已经尝试了谷歌返回的每个答案,添加了明确的 FK 引用,FK 不再可以为空,删除了虚拟关键字,没有任何区别。

非常感谢任何指导/故障排除步骤。

【问题讨论】:

我看到 GuardianPermission 类具有将属性设置为默认值的无参数构造函数。所以我假设 GuardianModel 也在做同样的事情。所以也许这会有所帮助:docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/… 如果有人想尝试一下,如果你放一些代码而不是图像会很好。 【参考方案1】:

好的,所以我发表我的评论作为答案:

EF Core 5.0 中的重大更改 "非空引用导航不会被查询覆盖"

https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/breaking-changes#nonnullreferences

即如果导航属性为 not null - 它不会由 EF 填充 (空集合除外)。

来自文档:

旧行为 在 EF Core 3.1 中,急切地初始化为非空值的引用导航有时会被数据库中的实体实例覆盖,无论键值是否匹配。但是,在其他情况下,EF Core 3.1 会做相反的事情并保留现有的非空值。

新行为 从 EF Core 5.0 开始,非空引用导航永远不会被查询返回的实例覆盖。

请注意,仍然支持将集合导航急切初始化到空集合。

【讨论】:

【参考方案2】:

根据@apocalypse 的建议,它与https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-5.0/breaking-changes#nonnullreferences 中的重大更改有关:

非空引用导航不会被查询覆盖 跟踪问题 #2693

旧行为 在 EF Core 3.1 中,急切地初始化为非空值的引用导航有时会被数据库中的实体实例覆盖,无论键值是否匹配。但是,在其他情况下,EF Core 3.1 会做相反的事情并保留现有的非空值。

新行为 从 EF Core 5.0 开始,非空引用导航永远不会被查询返回的实例覆盖。

请注意,仍然支持将集合导航到空集合的急切初始化。

为什么 将引用导航属性初始化为“空”实体实例会导致状态不明确。例如:

C#

public class Blog

    public int Id  get; set; 
    public Author Author  get; set; ) = new Author();

通常,对博客和作者的查询将首先创建博客实例,然后根据从数据库返回的数据设置适当的作者实例。但是,在这种情况下,每个 Blog.Author 属性都已初始化为空的 Author。除了 EF Core 没有办法知道这个实例是“空的”。所以覆盖这个实例可能会默默地丢弃一个有效的作者。因此,EF Core 5.0 现在始终不会覆盖已初始化的导航。

这种新行为在大多数情况下也与 EF6 的行为一致,尽管在调查后我们也发现了一些与 EF6 不一致的情况。

缓解措施 如果遇到此中断,则解决方法是停止急切地初始化引用导航属性。

就我而言:

  public GuardianModel() : base()

    Permissions = **new GuardianPermission();**
    Active = true;

上面有问题的行加粗。我的解决方案是删除默认初始化并确保在主类实例化后实例化依赖类。有没有更好的办法。即:

var g = new GuardianModel();
g.Permissions = new GuardianPermission();

必须有比必须找到所有上述实例更好的方法。

【讨论】:

我对 EF 不熟悉,但你可以制作一个工厂方法。 GuardianModel.Create() 其中Create 是执行此操作的静态方法var g = new GuardianModel(); g.Permissions = new GuardianPermission(); return g;

以上是关于EF Core 5.0.4 - 从核心 3.1 升级后,通过 Include() 的急切加载不起作用的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 ASP.NET Core 3.1 控制器会自动为从视图返回的 EF Core 模型分配 ID?

3.1 ef core

如何在 EF Core 3.1 中使用脚手架流程更新我的实体 - 执行超时已过期

EF-Core:表“名称”已经存在 - 尝试更新数据库时

无法从 Linq 查询 EF Core 访问字段值

EF Core 3.1 无法查询 Json 序列化对象