我在 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]
为什么 Entity Framework 有一个额外的(无用的)左连接用于这种类型的关系?
当你有一对一的关系时,即使你没有明确
.Include
相关的实体,实体框架也会创建一个连接语句。
在您的情况下,第一个连接来自
.Include
语句,默认情况下添加另一个左外部连接。您可以通过删除 include 语句并观察将生成“Required”左外连接语句的 SQL 输出来检查这一点(实体框架需要它来验证对象模型是否正确)。
此外,从 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 => e.Level1Reference.Level2Reference)
出现这种情况时,它会生成“required”的join,然后为Level1Reference生成join。然后它需要生成一个连接:
此问题的一个解决方法是使用 LINQ 语法并将数据库键公开为对象模型的属性。执行此操作时,您实际上覆盖了对象导航语法并手动告诉 EF 如何构建连接。由于它没有查找如何使用您的 Fluent 对象模型构建连接,因此它别无选择,只能按照您的连接说明进行操作。 (这是当时的项目经理 Diego Vega 给我的大概解释,当时我们提交了几个关于重复/不必要连接的错误)。