实体框架(4.2)HasRequired导致意外的LEFT OUTER JOIN

时间:2011-12-15 23:08:43

标签: linq entity-framework include dbcontext

在为第一个定义的导航属性构建连接时,实体框架(来自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属性,但这没有效果。

这是一个错误还是我可能错误配置了什么?建议?

3 个答案:

答案 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()