9

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

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

4

3 回答 3

7

您的模型配置是正确的,我认为这不是错误,而是设计行为,但我无法确切说明是什么设计。我还在此类查询中看到了该 SQL。只有几点说明:

  • 您看到的查询并非特定于 EF 4.2。EF 4.1 和 EF 4.0 也会出现这种情况。但不适用于 EF 1 (.NET 3.5)。在 EF 1 中,每一个Include,也是第一个,已映射到 a LEFT OUTER JOIN,也用于所需的关系。

  • 我认为不能说INNER JOIN对所需的导航属性使用“正确”LEFT OUTER JOIN是错误的。从映射的角度来看,使用什么并不重要,因为数据库中的约束正确地表示了模型中的关系。对于必需的导航属性,数据库中的 FK 列不能为空,并且数据库中必须有一个约束,强制 FK 引用目标表中的现有行。如果是这种情况,JOIN无论您使用INNER JOINor ,每个都必须返回一行LEFT OUTER JOIN

  • 如果模型和数据库在关系方面“不同步”会发生什么?在这两种情况下基本上都会发生废话:如果您使用 aLEFT OUTER JOIN并且 FKNULL在数据库中或引用不存在的行,您将获得导航属性所在的实体,这null违反了需要该属性的模型定义。使用 anINNER JOIN并不是更好:您根本不会得到任何实体,查询结果至少与使用 的结果一样错误LEFT OUTER JOIN,如果不是更糟的话。

  • 因此,我认为 .NET 4 中将INNER JOINs 用于某些Includes 的更改不是因为 EF 1 中的 SQL 错误,而是为了创建更好、性能更高的 SQL。此更改实际上引入了一项重大更改,因为某些查询现在返回的结果与 EF 1 中不同:http: //thedatafarm.com/blog/data-access/ef4-break-change-ef4-inner-joins-affect-急切加载多对多/

  • 我的理解是,这已得到修复,原因是INNER JOIN在 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/候选版本相比)以避免像这样的重大变化。

抱歉,这更像是一个历史故事,而不是一个真实的解释,为什么你会得到一个INNER JOIN然后是一个LEFT OUTER JOIN。如前所述,以这种方式编写查询并没有错——因为使用两个INNER JOINs 或两个LEFT OUTER JOINs 也没有错。我想只有 EF 团队才能准确解释为什么您的查询会产生特定的 SQL。

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

于 2011-12-16T20:14:33.510 回答
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 分钟以上

于 2013-10-23T17:56:44.500 回答
0

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

于 2019-03-21T02:35:31.770 回答