【问题标题】:Eager Loading with join急切加载与连接
【发布时间】:2015-10-05 18:42:08
【问题描述】:

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

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 正在为连接生成一个查询,然后执行第二个查询来填充连接返回的每条记录的导航属性。

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

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

任何想法我做错了什么?

【问题讨论】:

  • using System.Data.Entity; 将增加对 Include 的 lambda 表达式重载的支持
  • 这让 lambda 表达式正常工作,但查询仍然是延迟加载...
  • 您是否尝试过仅对没有包含的锁进行投影甚至更好...手动将您的连接添加到锁?

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


【解决方案1】:

这就是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 是按需加载的。

幸运的是,relationship fixup 提供了一条出路,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

【讨论】:

  • @GertArnold - 我遵循您上面描述的模式并且导航属性没有填充。因此,它对我不起作用。
  • 也许值得提出一个新问题。
  • 感谢您的句子:“如果您在查询末尾应用它,则包含始终有效”
猜你喜欢
  • 2014-08-07
  • 2020-12-20
  • 1970-01-01
  • 2015-09-30
  • 2015-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多