使用 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
。
我有一个连接两个 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
的一件事是
这似乎让预测 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
。