渴望加载加入

Jef*_*ege 5 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 }
    );
Run Code Online (Sandbox Code Playgroud)

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

微软的文档解释了这个问题: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 }
    );
Run Code Online (Sandbox Code Playgroud)

它仍然为每个作业锁生成一个查询.

我有什么想法我做错了吗?

Ger*_*old 6

这就是问题所在Include.使它无效是太容易了,并不总是清楚为什么它不起作用.打破一个问题的Include是改变查询的形状.另一个是投射到非实体类型或匿名类型.

这似乎令人难以置信地预测何时有效Include和无效,但有一个简单的诀窍:Include如果您在查询结束时应用它,则始终有效.

如果你不能在那里申请,那么无论如何都不会有效.

考虑到这一点,如果我们看一下你的情况,很明显为什么Include不起作用.你做不到

(... select new { m, j }).Include("locks"); // Runtime error
Run Code Online (Sandbox Code Playgroud)

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

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

所以你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 });
Run Code Online (Sandbox Code Playgroud)

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

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

  • 也许值得一个新问题. (2认同)