使用 join 进行预加载

Eager Loading with join

我有一个连接两个 table 的查询,这两个 table 在 Entity Framework 中没有定义的关系,并且连接的 table 具有一对多导航 属性 到第三个 table.

msg 和 job 之间存在一对多关系,但没有外键,也没有在 .EDMX 中定义关联。

job和lock是一对多的关系,.EDMX中定义了关联,所以job有一个job.locks导航属性m,lock有一个lock.job导航属性.

我的原始查询:

var msgsAndJobs = (
    from m in dbContext.msgs
    join j in dbContext.jobs
        on new { jobid = m.jobid, priority = m.priority }
        equals new { jobid = j.jobid, priority = j.priority }
    where m.msgtype == "EMERGENCY"
    orderby new { m.recvdt }
    select new { m, j }
    );

我发现 EF 正在为连接生成一个查询,然后执行第二个查询以填充连接返回的每条记录的导航 属性。

Microsoft 的文档解释了这个问题:https://msdn.microsoft.com/en-us/data/jj574232.aspx

所以我认为我可以使用 .Include() 子句来急切加载所涉及的记录。但它似乎没有用:

我的新查询:

var msgsAndJobs = (
    from m in dbContext.msgs
    join j in dbContext.jobs.Include("locks")
        on new { jobid = m.jobid, priority = m.priority }
        equals new { jobid = j.jobid, priority = j.priority }
    where m.msgtype == "EMERGENCY"
    orderby new { m.recvdt }
    select new { m, j }
    );

而且它仍在为每个作业锁生成一个查询。

知道我做错了什么吗?

这就是 Include 的问题。让它失效太容易了,而且它为什么不起作用并不总是很清楚。打破 Include 的一件事是 . Another one is projecting to a non-entity type or an anonymous type.

这似乎让预测 Include 何时起作用和不起作用变得异常困难,但有一个简单的技巧:如果应用它,Include 总是起作用在查询结束时。

如果你不能在那里应用它,它无论如何都不会有效。

考虑到这一点,如果我们查看您的案例,就会很清楚为什么 Include 不起作用。你做不到

(... select new { m, j }).Include("locks"); // Runtime error

因为 locks 显然不是匿名类型的导航 属性。如果使用 lambda 版本更明显:

(... select new { m, j }).Include(x => x.locks); // Doesn't compile

所以你的Include是无效的,locks是按需加载的。

幸运的是,由于 关系修复,EF 通过其导航属性将上下文中的实体编织在一起的过程,因此有一条出路。将您的查询更改为:

var msgsAndJobs = (
    from m in dbContext.msgs
    join j in dbContext.jobs
        on new { jobid = m.jobid, priority = m.priority }
        equals new { jobid = j.jobid, priority = j.priority }
    where m.msgtype == "EMERGENCY"
    orderby new { m.recvdt }
    select new { m, j, j.locks }
    ).AsEnumerable()
    .Select(x => new { x.m, x.j });

如果您执行此查询,.AsEnumerable() 会将结果加载到上下文中,之后您可以 select 您最初想要的结果。现在您会注意到 EF 已填充所有 job.locks 个集合。

但有一件重要的事情:您必须禁用延迟加载,否则寻址job.locks 集合仍会触发延迟加载。这是因为,即使集合已填充,它也没有在内部标记为 Loaded