实体框架和强制内部联接

时间:2011-10-03 19:01:01

标签: c#-4.0 linq-to-entities entity-framework-4.1

我的Table1具有以下关系(它们不是强制执行的,只是为导航属性创建关系)

Table1 (*)->(1) Table2
Table1 (*)->(1) Table3
Table1 (*)->(1) Table4
Table1 (*)->(1) Table5

使用急切加载代码如

IQueryable<Table1> query = context.Table1s;

query = query.Include(Table1 => Table1.Table2);
query = query.Include(Table1 => Table1.Table3);
query = query.Include(Table1 => Table1.Table4);
query = query.Include(Table1 => Table1.Table5);

query = query.Where(row => row.Table1Id == table1Id);

query.Single();

我尝试组织Include()语句的每一种方式,包含的第一个表在其生成的TSQL中都有一个Inner Join,剩下的就是Left Outer Join(我希望所有这些都是Left Outer)。我不是实体分裂,它们只是FK的普通表。

如果DefaultIfEmpty()是唯一的解决方案,有人可以解释为什么除了包含的第一个表之外的所有表都提供了预期的SQL吗?

我的理解是导航属性的默认行为是LEFT OUTER但我无法获得所有属性来生成默认值。

任何帮助都会非常感激。

提前谢谢!

-----创建TSQL(为简洁而修改,但结构相同)-------

(@p__linq__0 int)SELECT 
[Limit1].[Table1Id] AS [Table1Id], 
[Limit1].[OtherData] AS [OtherData]
FROM ( SELECT TOP (2) 
    [Extent1].[Table1Id] AS [Table1Id], 
    [Extent1].[OtherData] As [OtherData]
    FROM       [dbo].[Table1] AS [Extent1]
    INNER JOIN [dbo].[Table2] AS [Extent2] ON [Extent1].[Table2Id] = [Extent2].[Table2Id]
    LEFT OUTER JOIN [dbo].[Table3] AS [Extent3] ON [Extent1].[Table3Id] = [Extent3].[Table3Id]
    LEFT OUTER JOIN [dbo].[Table4] AS [Extent4] ON [Extent1].[Table4Id] = [Extent4].[Table4Id]
    LEFT OUTER JOIN [dbo].[Table5] AS [Extent5] ON [Extent1].[Table5Id] = [Extent5].[Table5Id]
    WHERE [Extent1].[Table1Id] = @p__linq__0
)  AS [Limit1]

3 个答案:

答案 0 :(得分:20)

EF似乎使用INNER JOIN来包含必需LEFT OUTER JOIN以包含可选导航属性。例如:

public class Order
{
    public int Id { get; set; }
    public string Details { get; set; }
    public Customer Customer { get; set; }
}

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
}

如果我在Customer上将Order定义为必需的属性...

public class MyContext : DbContext
{
    public DbSet<Order> Orders { get; set; }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Order>()
            .HasRequired(o => o.Customer)
            .WithMany();
    }
}

...并发出此查询...

using (var ctx = new MyContext())
{
    var result = ctx.Orders
        .Include(o => o.Customer)
        .Where(o => o.Details == "Peanuts")
        .FirstOrDefault();
}

...我得到这个SQL:

SELECT TOP (1) 
[Extent1].[Id] AS [Id], 
[Extent1].[Details] AS [Details], 
[Extent2].[Id] AS [Id1], 
[Extent2].[Name] AS [Name]
FROM  [dbo].[Orders] AS [Extent1]
INNER JOIN [dbo].[Customers] AS [Extent2] 
    ON [Extent1].[Customer_Id] = [Extent2].[Id]
WHERE N'Peanuts' = [Extent1].[Details]

如果我将模型配置.HasRequired(o => o.Customer)更改为...

.HasOptional(o => o.Customer)

...除了将INNER JOIN [dbo].[Customers] AS [Extent2]替换为:

之外,我得到完全相同的查询
LEFT OUTER JOIN [dbo].[Customers] AS [Extent2]

从模型角度来看,它是有道理的,因为如果您将关系定义为必需,则表示永远不会有Order没有Customer。如果您通过删除数据库中的强制执行来规避此要求,并且如果您确实拥有了没有客户的订单,则会违反您自己的模型定义。

如果遇到这种情况,只有解决方案可能会使关系可选。我不认为可以控制使用Include时创建的SQL。

答案 1 :(得分:3)

在执行IQueryable.Include()时,如果没有任何导航属性基于强制关系,EF中的

,那么EF将使用第一个表。它期望在模式中强制执行至少一个关系,并且首先应使用IQueryable.Include()对其进行编码,然后使用Include()

添加其他表

答案 2 :(得分:0)

如果您有一个表结构,如何强制Entity Framework执行内部联接:

  • 学生可以有时间表,但不必
  • 时间表可以有课程,但不必
  • 课程必须有课程
  • 课程必须有测试

如果您想查找已通过特定测试的学生,您可以在逻辑上执行以下操作:

requiretty

关键在于你从StudentEntity开始,并根据链中的连接放入一些条件。但由于学生安排是可选的,因此E.F.生成LEFT OUTER Joins。

相反,你应该开始降低链条并建立起来。例如:

var studentsWhoPassed = context.Set<StudentEntity>()
    .Where(x => x.Something)
    .Include(x => x.Schedules.Select(y => y.Classes.Select(z => z.Tests)))
    .Etc().Etc()

当您尝试查询具有测试标准的学生时,从类开始是很奇怪的。但它实际上使LINQ更简单。

由于学生没有时间表,但是...一个班级必须有考试,班级必须有一个班级ID,而班级必须有一个学生ID,你得到内部联接所有四处走动。

当然,这个学校的例子是抽象的,但这个想法适用于我使用相同类型的关系的其他例子。