渴望加载加入

时间:2015-10-05 18:42:08

标签: c# sql-server entity-framework lazy-loading

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

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

作业和锁之间存在一对多关系,并且.EDMX中定义了关联,因此作业具有job.locks导航属性,并且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 }
    );

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

任何想法我做错了什么?

1 个答案:

答案 0 :(得分:5)

这是Include的问题。使它无效是太容易了,并不总是清楚为什么它不起作用。打破Include的一件事是changing the shape of the query。另一个是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()会将结果加载到上下文中,之后您可以选择最初想要的结果。现在您将注意到EF已填充所有job.locks个集合。

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