实体框架和强制内连接

发布于 2024-12-08 00:32:45 字数 1546 浏览 0 评论 0原文

我的 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 和其余的是左外连接(我希望所有这些都是左外连接)。我不是实体拆分,它们只是带有 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]

I have Table1 with the following relationships (they are not enforced they only create the relationship for the navigation properties)

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

Using eager loading code looks like

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();

Every way I try to organize the Include() statements, the first table included has an Inner Join in its generated TSQL and the remaining are Left Outer Join (I expect Left Outer for all of them). I am not Entity Splitting, they are just plain tables with FKs.

If DefaultIfEmpty() is the only solution, can someone explain the reason why when all but the first table included provide the SQL expected?

My understanding is that default behavior for a Navigation Property is LEFT OUTER but I cannot get ALL properties to generate the default.

Any help would be MUCH appreciated.

Thank you in advance!

----- Created TSQL (modified for brevity but structure the same) -------

(@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]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

贱贱哒 2024-12-15 00:32:45

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 上的 required 属性...

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();
}

...I得到这个 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]

From模型观点这是有道理的,因为您说如果您将关系定义为必需,那么没有客户就永远不会有订单。如果您通过删除数据库中的强制措施来规避此要求,并且如果您实际上有没有客户的订单,那么您就违反了您自己的模型定义。

如果您遇到这种情况,唯一的解决方案可能是使这种关系成为可选。我认为无法控制使用 Include 时创建的 SQL。

EF seems to use INNER JOIN for including a required and LEFT OUTER JOIN for including an optional navigation property. Example:

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; }
}

If I define Customer as a required property on 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();
    }
}

...and issue this query...

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

...I get this 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]

If I change in the model configuration .HasRequired(o => o.Customer) to...

.HasOptional(o => o.Customer)

... I get exactly the same query except that INNER JOIN [dbo].[Customers] AS [Extent2] is replaced by:

LEFT OUTER JOIN [dbo].[Customers] AS [Extent2]

From model viewpoint it makes sense because you are saying that there can never be an Order without a Customer if you define the relationship as required. If you circumvent this requirement by removing the enforcement in the database and if you actually have then orders without a customer you violate your own model definition.

Only solution is likely to make the relationship optional if you have that situation. I don't think it is possible to control the SQL that is created when you use Include.

奢华的一滴泪 2024-12-15 00:32:45

在 EF 中执行 IQueryable.Include() 时,如果没有导航属性基于强制关系,则 EF 将使用第一个表。它期望在架构中至少强制执行一种关系,并且应首先使用 IQueryable.Include() 编码关系,然后使用 Include() 添加其他表代码>

in EF when doing IQueryable.Include() if none of the navigation properties are based on an enforced relationship then EF will use the first table. It expects that at least one of the relationships is enforced in the schema and that one should be coded with the IQueryable.Include() first, then add the other tables with Include()

分开我的手 2024-12-15 00:32:45

如果您有一个表结构,如何强制实体框架进行内联接:

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

当您想要查找已通过特定测试的学生,您在逻辑上会执行类似以下操作:

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

重点是您从 StudentEntity 开始,并根据链下的连接添加一些条件。但由于要安排的学生是可选的,因此 EF 会生成 LEFT OUTER Join。

相反,你应该从链条的下游开始并逐步建立。例如:

var studentsWhoPassed = context.Set<ClassEntity>()
    .Where(class => class.Tests.Any(test => test.Status == Status.Passed)
        && class.Schedule.Student.Something == studentSomething)
    .Include(class => class.Schedule.Student)

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

因为学生不必有时间表,但是...班级必须有测试,班级必须有 ScheduleID,时间表必须有 StudentID,所以您可以一直获得内部联接。

诚然,这个学校的例子很抽象,但这个想法适用于我处理过相同类型关系的其他例子。

How to force Entity Framework to do Inner Joins if you have a table structure such that:

  • Students can have Schedules, but don't have to
  • Schedules can have classes, but don't have to
  • Classes MUST have Curriculums
  • Curriculums MUST have Tests

When you want to look up Students who have passed particular Tests, you would logically do something like:

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

The point being that you start with a StudentEntity and put in some conditions based on the joins down the chain. But because a Student to Schedule is optional, E.F. generates LEFT OUTER Joins.

Instead, you should start lower down the chain and build up. For example:

var studentsWhoPassed = context.Set<ClassEntity>()
    .Where(class => class.Tests.Any(test => test.Status == Status.Passed)
        && class.Schedule.Student.Something == studentSomething)
    .Include(class => class.Schedule.Student)

It is weird to start with a Class when you are trying to query for Students with Test Criteria. But it actually makes the LINQ simpler.

Because of the Student does not have to have a Schedule, but... a Class has to have Test(s), and a Class must have a ScheduleID, and Schedules must have a StudentID, you get Inner Joins all the way around.

Granted, this school example is abstract, but the idea holds true to other examples I have worked with the same types of relationships.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文