繁体   English   中英

当它们相互交叉引用时,如何在实体框架中加载嵌套实体?

[英]How do I load nested entities in entity framework when they cross reference each other?

这个有点复杂,所以我创建了一个示例项目(自述文件中的说明)

https://github.com/dominicshaw/EntityFrameworkNestingQuirk

我有以下代码:

private async Task<(bool Found, Appraisal Appraisal)> Get(int id)
{
    var staff = await _context.Staff.FindAsync(3);

    _logger.LogInformation("Got Staff Object   : Staff {id} has manager set to {managerId} - running query for appraisal", staff.Id, staff.ManagerId);

    var appraisal =
        await _context.Appraisals
            .Include(ap => ap.Staff).ThenInclude(s => s.Manager)
            .Include(ap => ap.Staff).ThenInclude(s => s.SecondaryManager)
            .Where(ap => ap.Id == id)
            .SingleOrDefaultAsync();

    _logger.LogInformation("Appraisal Query Run: Staff {id} has manager set to {managerId} - run completed", staff.Id, staff.ManagerId);

    if (appraisal != null)
    {
        _logger.LogInformation("Appraisal->Staff->2ndManager->Manager={id} (We should NOT have this)", appraisal.Staff.SecondaryManager?.ManagerId);
        return (true, appraisal);
    }

    return (false, null);
}

数据永远不会被写入,并且员工 id 3 有两个经理,都设置在第一个记录行的位置。

然后我从数据库中获取该员工的评估 - 查询 ef 生成看起来不错并带回两个经理,但是在运行该查询时,我丢失了员工 object (3) 的经理 ID(应该是 1)。

命中第二条日志时,managerid 为 null。

我注意到 EF model 已加载管理器的辅助管理器,即使我没有要求它(除非我的 ef 查询语法错误?)。 经理 (1) 是 2 和 3 的经理,所以这是正确的,但它应该已经加载到 Appraisal->Staff->Manager,而不是 Appraisal->Staff->SecondaryManager->Manager

有什么想法可以解决这个问题吗?

问题在于 EF Core 认为ManagerSecondaryManager自引用关系是一对一的关系,这进一步意味着它认为它只能将给定实体分配给单个导航属性。

以下OnModelCreating()配置在本地为我解决了这个问题。 与您提供的配置相比,仅更改了注释行:

builder.Entity<Staff>().HasIndex(e => e.ManagerId).IsUnique(false);
    builder.Entity<Staff>()
        .HasOne(a => a.Manager)
        .WithMany() // Changed
        .HasForeignKey(s => s.ManagerId)
        .IsRequired(false)
        .OnDelete(DeleteBehavior.NoAction);
    
    builder.Entity<Staff>().HasIndex(e => e.SecondaryManagerId).IsUnique(false);
    builder.Entity<Staff>()
        .HasOne(a => a.SecondaryManager)
        .WithMany() // Changed
        .HasForeignKey(s => s.SecondaryManagerId)
        .IsRequired(false)
        .OnDelete(DeleteBehavior.NoAction);

为什么要填充嵌套的Manager

原因是 EF Core 的“关系修复”行为,这意味着如果您加载导航属性引用的实体,它会自动将该实体分配给给定的导航属性。

即使您运行两个完全独立的查询,如果单独加载的实体之间存在关系连接,EF Core 也会在 memory 中将它们连接在一起。

在此示例中,主 Manager 和嵌套 Manager 都引用了 ID 为 1 的相同 Staff 实体,因此它将在 memory 中“修复”两者。 但是,它碰巧首先“修复”了嵌套的一个,并且考虑到一对一的约束,我想它只是停在那里。 也许对 EF Core 内部工作原理有更深入了解的人会用更多细节来启发我们。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM