在查询TPT继承中的基类时,实体框架为什么包含具有外部连接的派生类?

本文关键字:外部 包含具 为什么 连接 派生 框架 实体 继承 TPT 查询 基类 | 更新日期: 2023-09-27 18:05:28

我使用实体框架6与TPT(表每类型)继承。

我有这个类

public class Person
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}
public class Customer : Person
{
    public string BillingAddress { get; set; }
}    
public class IndividualCustomer : Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime Birthday { get; set; }
}
public class CorporateCustomer : Customer
{
    public string ContactPerson { get; set; }
    public string CorporateTaxNo { get; set; }
}
public class SalesInvoice
{
    public Guid Id { get; set; }
    public Guid CustomerId { get; set; }
    public Customer Customer { get; set; }
}

当我执行这个查询

var people = ctx.People.ToList();

实体框架生成这个查询

SELECT 
    CASE WHEN (( NOT (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN '0X' WHEN (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)) THEN '0X0X0X' WHEN (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL) AND ( NOT (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL))) AND ( NOT (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)))) THEN '0X0X' WHEN (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL)) THEN '0X0X1X' ELSE '0X1X' END AS [C1], 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name], 
    CASE WHEN (( NOT (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)) THEN [Project4].[BillingAddress] WHEN (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL) AND ( NOT (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL))) AND ( NOT (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)))) THEN [Project4].[BillingAddress] WHEN (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL)) THEN [Project4].[BillingAddress] END AS [C2], 
    CASE WHEN (( NOT (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)) THEN [Project4].[ContactPerson] WHEN (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL) AND ( NOT (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL))) AND ( NOT (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL)) THEN CAST(NULL AS varchar(1)) END AS [C3], 
    CASE WHEN (( NOT (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)) THEN [Project4].[CorporateTaxNo] WHEN (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL) AND ( NOT (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL))) AND ( NOT (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL)) THEN CAST(NULL AS varchar(1)) END AS [C4], 
    CASE WHEN (( NOT (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL) AND ( NOT (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL))) AND ( NOT (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL)) THEN [Project4].[FirstName] END AS [C5], 
    CASE WHEN (( NOT (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL) AND ( NOT (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL))) AND ( NOT (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL)) THEN [Project4].[LastName] END AS [C6], 
    CASE WHEN (( NOT (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS datetime2) WHEN (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)) THEN CAST(NULL AS datetime2) WHEN (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL) AND ( NOT (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL))) AND ( NOT (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)))) THEN CAST(NULL AS datetime2) WHEN (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL)) THEN [Project4].[Birthday] END AS [C7], 
    CASE WHEN (( NOT (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C1] = 1) AND ([Project4].[C1] IS NOT NULL) AND ( NOT (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL))) AND ( NOT (([Project4].[C3] = 1) AND ([Project4].[C3] IS NOT NULL)))) THEN CAST(NULL AS varchar(1)) WHEN (([Project4].[C2] = 1) AND ([Project4].[C2] IS NOT NULL)) THEN CAST(NULL AS varchar(1)) ELSE [Project1].[Address] END AS [C8]
    FROM   [dbo].[Person] AS [Extent1]
    LEFT OUTER JOIN  (SELECT 
        [Extent2].[Id] AS [Id], 
        [Extent2].[Address] AS [Address], 
        cast(1 as bit) AS [C1]
        FROM [dbo].[Vendor] AS [Extent2] ) AS [Project1] ON [Extent1].[Id] = [Project1].[Id]
    LEFT OUTER JOIN  (SELECT 
        [Extent3].[Id] AS [Id], 
        [Extent3].[BillingAddress] AS [BillingAddress], 
        cast(1 as bit) AS [C1], 
        [Project2].[ContactPerson] AS [ContactPerson], 
        [Project2].[CorporateTaxNo] AS [CorporateTaxNo], 
        [Project3].[FirstName] AS [FirstName], 
        [Project3].[LastName] AS [LastName], 
        [Project3].[Birthday] AS [Birthday], 
        CASE WHEN (([Project3].[C1] = 1) AND ([Project3].[C1] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Project3].[C1] = 1) AND ([Project3].[C1] IS NOT NULL))) THEN cast(0 as bit) END AS [C2], 
        CASE WHEN (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Project2].[C1] = 1) AND ([Project2].[C1] IS NOT NULL))) THEN cast(0 as bit) END AS [C3]
        FROM   [dbo].[Customer] AS [Extent3]
        LEFT OUTER JOIN  (SELECT 
            [Extent4].[Id] AS [Id], 
            [Extent4].[ContactPerson] AS [ContactPerson], 
            [Extent4].[CorporateTaxNo] AS [CorporateTaxNo], 
            cast(1 as bit) AS [C1]
            FROM [dbo].[CorporateCustomer] AS [Extent4] ) AS [Project2] ON [Extent3].[Id] = [Project2].[Id]
        LEFT OUTER JOIN  (SELECT 
            [Extent5].[Id] AS [Id], 
            [Extent5].[FirstName] AS [FirstName], 
            [Extent5].[LastName] AS [LastName], 
            [Extent5].[Birthday] AS [Birthday], 
            cast(1 as bit) AS [C1]
            FROM [dbo].[IndividualCustomer] AS [Extent5] ) AS [Project3] ON [Extent3].[Id] = [Project3].[Id] ) AS [Project4] ON [Extent1].[Id] = [Project4].[Id]
go

当我想要的只是person类时,实体框架为什么要添加所有这些外部连接?

但是如果我指定我想要返回的字段,实体框架返回一个更好的查询

var people = ctx.People.Select(x => new {x.Id, x.Name}).ToList();

返回
SELECT 
    1 AS [C1], 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Name] AS [Name]
    FROM [dbo].[Person] AS [Extent1]
go

当使用include

时,更难修改查询。
var invoices = ctx.SalesInvoices.Include("Customer").ToList();

返回
SELECT 
    1 AS [C1], 
    [Extent1].[Id] AS [Id], 
    [Extent1].[CustomerId] AS [CustomerId], 
    [Extent1].[CustomerId2] AS [CustomerId2], 
    CASE WHEN ([Join3].[Id1] IS NULL) THEN CAST(NULL AS varchar(1)) WHEN (((CASE WHEN (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL))) THEN cast(0 as bit) END) <> 1) AND ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) <> 1)) THEN '2X0X' WHEN ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) = 1) THEN '2X0X0X' ELSE '2X0X1X' END AS [C2], 
    [Join3].[Id1] AS [Id1], 
    [Join3].[Name] AS [Name], 
    [Join3].[BillingAddress] AS [BillingAddress], 
    CASE WHEN ([Join3].[Id1] IS NULL) THEN CAST(NULL AS varchar(1)) WHEN (((CASE WHEN (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL))) THEN cast(0 as bit) END) <> 1) AND ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) <> 1)) THEN CAST(NULL AS varchar(1)) WHEN ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) = 1) THEN [Join3].[ContactPerson] END AS [C3], 
    CASE WHEN ([Join3].[Id1] IS NULL) THEN CAST(NULL AS varchar(1)) WHEN (((CASE WHEN (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL))) THEN cast(0 as bit) END) <> 1) AND ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) <> 1)) THEN CAST(NULL AS varchar(1)) WHEN ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) = 1) THEN [Join3].[CorporateTaxNo] END AS [C4], 
    CASE WHEN ([Join3].[Id1] IS NULL) THEN CAST(NULL AS varchar(1)) WHEN (((CASE WHEN (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL))) THEN cast(0 as bit) END) <> 1) AND ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) <> 1)) THEN CAST(NULL AS varchar(1)) WHEN ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) = 1) THEN CAST(NULL AS varchar(1)) ELSE [Join3].[FirstName] END AS [C5], 
    CASE WHEN ([Join3].[Id1] IS NULL) THEN CAST(NULL AS varchar(1)) WHEN (((CASE WHEN (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL))) THEN cast(0 as bit) END) <> 1) AND ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) <> 1)) THEN CAST(NULL AS varchar(1)) WHEN ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) = 1) THEN CAST(NULL AS varchar(1)) ELSE [Join3].[LastName] END AS [C6], 
    CASE WHEN ([Join3].[Id1] IS NULL) THEN CAST(NULL AS datetime2) WHEN (((CASE WHEN (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C11] = 1) AND ([Join3].[C11] IS NOT NULL))) THEN cast(0 as bit) END) <> 1) AND ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) <> 1)) THEN CAST(NULL AS datetime2) WHEN ((CASE WHEN (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL)) THEN cast(1 as bit) WHEN ( NOT (([Join3].[C12] = 1) AND ([Join3].[C12] IS NOT NULL))) THEN cast(0 as bit) END) = 1) THEN CAST(NULL AS datetime2) ELSE [Join3].[Birthday] END AS [C7]
    FROM  [dbo].[SalesInvoice] AS [Extent1]
    LEFT OUTER JOIN  (SELECT [Extent2].[Id] AS [Id1], [Extent2].[BillingAddress] AS [BillingAddress], [Project1].[ContactPerson] AS [ContactPerson], [Project1].[CorporateTaxNo] AS [CorporateTaxNo], [Project1].[C1] AS [C12], [Project2].[FirstName] AS [FirstName], [Project2].[LastName] AS [LastName], [Project2].[Birthday] AS [Birthday], [Project2].[C1] AS [C11], [Extent5].[Name] AS [Name]
        FROM    [dbo].[Customer] AS [Extent2]
        LEFT OUTER JOIN  (SELECT 
            [Extent3].[Id] AS [Id], 
            [Extent3].[ContactPerson] AS [ContactPerson], 
            [Extent3].[CorporateTaxNo] AS [CorporateTaxNo], 
            cast(1 as bit) AS [C1]
            FROM [dbo].[CorporateCustomer] AS [Extent3] ) AS [Project1] ON [Extent2].[Id] = [Project1].[Id]
        LEFT OUTER JOIN  (SELECT 
            [Extent4].[Id] AS [Id], 
            [Extent4].[FirstName] AS [FirstName], 
            [Extent4].[LastName] AS [LastName], 
            [Extent4].[Birthday] AS [Birthday], 
            cast(1 as bit) AS [C1]
            FROM [dbo].[IndividualCustomer] AS [Extent4] ) AS [Project2] ON [Extent2].[Id] = [Project2].[Id]
        INNER JOIN [dbo].[Person] AS [Extent5] ON [Extent2].[Id] = [Extent5].[Id] ) AS [Join3] ON [Extent1].[CustomerId] = [Join3].[Id1]
go

是否有解决这个问题的方法?

在查询TPT继承中的基类时,实体框架为什么包含具有外部连接的派生类?

当我想要的只是person类时,实体框架为什么要添加所有这些外部连接?

EF想要知道实体的确切类型(Person, Customer, IndividualCustomer等),以便能够创建正确的实例。因此,它只能从连接层次结构的所有表中获取类型信息(参见case语句)。

是否有解决这个问题的方法?

一种可能的替代方法是只应用-查询基类中需要的属性,就像您所做的那样。另一种可能的替代方法是使用每个层次表和标识符列。