在为第一个定义的导航属性构建连接时,实体框架(来自NuGet的最新版本)可能忽略了HasRequired配置。
例如,给定具有以下配置的POCO对象(Person):
var person = modelBuilder.Entity<Person>();
person.ToTable("The_Peoples");
person.HasKey(i => i.Id);
person.Property(i => i.Id).HasColumnName("the_people_id");
person.HasRequired(i => i.Address)
.WithMany()
.Map(map => map.MapKey("address_id"));
person.HasRequired(i => i.WorkPlace)
.WithMany()
.Map(map => map.MapKey("work_place_id"));
我正在尝试使用以下查询加载人员列表:
myContext.Set<People>()
.Include(o => o.Address)
.Include(o => o.WorkPlace);
实体框架生成以下查询:
FROM [dbo].[The_Peoples] AS [Extent1]
INNER JOIN [dbo].[The_Addresses] AS [Extent2] ON [Extent1].[address_id] = [Extent2].[address_id]
LEFT OUTER JOIN [dbo].[The_Work_Places] AS [Extent3] ON [Extent1].[work_place_id] = [Extent3].[work_place_id]
请注意,* The_Addresses *表的连接是内连接(如预期的那样),但是,* The_Work_Places *的后续连接是外连接。鉴于Address和WorkPlace属性都标记为必需,我希望两个连接都是内连接。我还尝试使用Required属性标记Address和WorkPlace属性,但这没有效果。
这是一个错误还是我可能错误配置了什么?建议?
答案 0 :(得分:7)
您的模型配置是正确的,我认为这不是一个错误,但它是设计行为,但我不知道究竟是什么设计。我也在这样的查询中看到过SQL。只有几点评论:
您看到的查询并非特定于EF 4.2。 EF 4.1和EF 4.0也会出现这种情况。但对于EF 1(.NET 3.5),不。在EF 1中,每个Include
(也是第一个)已映射到LEFT OUTER JOIN
,也用于所需的关系。
我认为不能说使用INNER JOIN
对于所需的导航属性是“正确的”而LEFT OUTER JOIN
是错误的。鉴于数据库中的约束正确表示模型中的关系,从映射视点来看,使用什么并不重要。对于必需的导航属性,数据库中的FK列不能为空,并且数据库中必须有一个约束,强制FK引用目标表中的现有行。如果是这种情况,则无论您使用JOIN
还是INNER JOIN
,每个LEFT OUTER JOIN
都必须返回一行。
如果模型和数据库关于关系“不同步”,会发生什么?在两种情况下基本上都是无意义的:如果你在数据库中使用LEFT OUTER JOIN
并且FK是NULL
或者引用了一个不存在的行,那么你将获得一个导航属性为{{1违反了属性所需的模型定义。使用null
并不是更好:你根本就没有实体,查询结果至少与INNER JOIN
的结果一样错误,如果不是更糟。
因此,我认为.NET 4中对某些LEFT OUTER JOIN
使用INNER JOIN
的更改并非因为EF 1中的SQL错误而是创建更好更多高性能的SQL。这一变化实际上引入了一个重大变化,因为现在一些查询返回了其他结果,而不是EF 1中的结果:http://thedatafarm.com/blog/data-access/ef4-breaking-change-ef4-inner-joins-affect-eager-loading-many-to-many/
我的理解是,这已得到修复,原因是在{4}中已经引入了Include
以便在EF 4中进行急切加载。(可能在此阶段(测试版/候选版)对于EF 4)你的查询将有两个INNER JOIN
。)EF团队对该问题的回复:http://connect.microsoft.com/VisualStudio/feedback/details/534675/ef4-include-method-returns-different-results-than-ef1-include(我的大胆亮点):
我们正在修复.net 4 RTM的问题。这是无意的 突破变化。 我们没有在每次离开的地方做出预期的改变 由Include生成的外连接成为.Net 4中的内连接。但是 相反,优化考虑了EF元数据中的约束 并试图转换那些可以安全的左外连接 根据约束转换为内部联接。我们有一个错误 基于所产生的约束我们推理的代码 在更积极的转换中,而不是约束所暗示的。 我们 缩减了优化,以便我们转换左外连接 只有在我们绝对可以肯定的地方进行内部联接 根据约束来做。我们认为我们可以改善这一点 将来优化一点。 你会开始看到更多 与RC和Beta相比,在RTM中为某些查询留下了外部联接 2但在大多数情况下,需要返回正确的结果。
因此,EF 4的最终版本显然重新引入了一些INNER JOIN
s(与beta / release候选版本相比),以避免像这样的重大变化。
对不起,这是一个历史故事,而不是一个真实的解释,为什么你得到LEFT OUTER JOIN
然后INNER JOIN
。如上所述,以这种方式编写查询并没有错 - 因为使用两个LEFT OUTER JOIN
或两个INNER JOIN
并不是错误的。我想只有EF团队才能解释为什么你的查询产生了那个特定的SQL。
我建议 - 如果你没有遇到严重的性能问题 - 不要担心SQL(因为你得到的结果毕竟是正确的)并继续。不喜欢EF创建的SQL,无论是编写大量功能和更改请求,还是编写大量原始SQL查询或完全放弃EF。
答案 1 :(得分:0)
这就像一个颈部,但今天我遇到了同样的问题......
这个问题:
FROM [dbo].[The_Peoples] AS [Extent1]
INNER JOIN [dbo].[The_Addresses] AS [Extent2] ON [Extent1].[address_id] = [Extent2].[address_id]
LEFT OUTER JOIN [dbo].[The_Work_Places] AS [Extent3] ON [Extent1].[work_place_id] = [Extent3].[work_place_id]
当您应用来自[dbo].[The_Peoples]
的过滤器时,您指定将过滤器应用于连接,因此需要的时间较少,但我们找到了(您可以运行查询计划以查看此内容)问题)是它连接到表的完整内容,然后它应用过滤器..这使得它需要很多额外的时间..在我们的情况下它触发超时,一个应该采取1的查询到3秒钟需要1分钟
答案 2 :(得分:0)
尝试使用WithRequiredDependent()
代替WithMany()