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

问题描述 投票:9回答:3

看起来实体框架(来自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属性,但这没有任何效果。

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

linq entity-framework include dbcontext
3个回答
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或者指的是不存在的行,你将得到一个导航属性为null的实体,违反了该属性所需的模型定义。使用INNER JOIN并不是更好:你根本就没有实体,查询结果至少与LEFT OUTER JOIN的结果一样错误,如果不是更糟的话。
  • 因此,我认为.NET 4中对某些INNER JOINs使用Includes的变化并不是因为EF 1中的SQL错误,而是创建更好,性能更高的SQL。这个改变实际上引入了一个突破性的变化,因为现在一些查询返回了其他结果,而不是它们在EF 1中所做的:http://thedatafarm.com/blog/data-access/ef4-breaking-change-ef4-inner-joins-affect-eager-loading-many-to-many/
  • 我的理解是,这已得到修复,原因是在很多情况下引入了INNER JOINs以便在EF 4中进行急切加载。(也许在这个阶段(EF 4的beta / release候选者),你的查询将有两个INNER JOINs 。)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 2相比,您将在RTM中开始查看更多左外连接,但在大多数情况下,需要返回正确的结果。 因此,EF 4的最终版本显然重新引入了更多的LEFT OUTER JOINs(与beta / release候选版本相比),以避免像这样的突破性变化。

对不起,这是一个历史故事,而不是一个真正的解释为什么你得到一个INNER JOIN然后LEFT OUTER JOIN。如上所述,以这种方式编写查询并没有错 - 因为使用两个INNER JOINs或两个LEFT OUTER JOINs并没有错。我想只有EF团队才能解释为什么你的查询产生了特定的SQL。

我建议 - 如果你没有遇到严重的性能问题 - 不要担心SQL(因为你得到的结果毕竟是正确的)并继续。不喜欢EF创建的SQL,最终会编写大量功能和更改请求,或者编写大量原始SQL查询或放弃EF。


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+分钟


0
投票

尝试使用WithRequiredDependent()而不是WithMany()

© www.soinside.com 2019 - 2024. All rights reserved.