实体框架在一对一(可选/必需)关系中生成第二个左联接

本文关键字:第二个 关系 一对一 框架 可选 必需 实体 | 更新日期: 2023-09-27 18:28:46

我在EF Code First中有以下模型:

public class A
{
    public int Id { get; set; }
    public virtual B { get; set; }
}
public class B
{
    public int Id { get; set; }
    public virtual A { get; set; }
}

我将关系定义如下:

modelBuilder.Entity<A>().HasKey(entity => entity.Id);
modelBuilder.Entity<B>().HasKey(entity => entity.Id);
modelBuilder.Entity<A>()
    .HasOptional(entity => entity.B)
    .WithRequired(entity => entity.A);

当我写下以下查询时:

var a = db.AItems.Include("B");

生成的查询如下:

SELECT
[Extent1].[Id] AS [Id],
[Extent3].[Id] AS [Id1]
FROM [dbo].[As] AS [Extent1]
LEFT OUTER JOIN [dbo].[Bs] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Id]
LEFT OUTER JOIN [dbo].[Bs] AS [Extent3] ON [Extent2].[Id] = [Extent3].[Id]

为什么实体框架对于这种类型的关系有一个附加的(无用的)左联接?

实体框架在一对一(可选/必需)关系中生成第二个左联接

当您有一对一的关系时,即使您没有显式.Include相关实体,实体框架也会创建一个联接语句。

在您的情况下,第一个联接来自.Include语句,默认情况下会添加另一个左侧外部联接。您可以通过删除include语句并观察SQL输出来检查这一点;"必需";left外部联接语句(实体框架需要它来验证对象模型是否正确)。

此外,从EF 6.4开始,如果您的查询是:,则无法避免重复联接

Set<Parent>
  .Include(x => x.RequiredChild.OptionalGrandChild)
  .ToList();

因为,.Include的XML文档注意到:

/// To include a reference and then a reference one level down: query.Include(e =&gt; e.Level1Reference.Level2Reference)

当这种情况发生时;"必需";join,然后为Level1Reference生成一个join。然后它需要为生成一个联接

  1. ";"必需";联接Level2Reference
  2. Include Level2Reference

解决此问题的一种方法是使用LINQ语法,并将数据库键作为对象模型上的属性公开。当您这样做时,您可以有效地覆盖对象导航语法,并手动告诉EF如何构建联接。由于它没有查找如何使用Fluent对象模型构建联接,因此它别无选择,只能遵循联接说明。(这是当时的项目经理Diego Vega给我的大致解释,当时我们提交了几个关于重复/不必要联接的错误)。