我有一个查询,它连接两个在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 }
);
它仍在为每个作业锁生成一个查询。
任何想法我做错了什么?
答案 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
。