EF简单的1- *关系导致奇怪的SQL查询

时间:2014-07-16 08:57:54

标签: c# sql entity-framework

我试图调试EF 6.1.1生成的SQL查询,并且我已经能够用这么少量的coude重现问题。这是一个简单的一对多单向关系,我试图加载:

模型如下:

public class Subscription
{
    public Guid Id { get; set; }
    public IList<Recipient> RecipientHistory { get; set; }
    public IList<Payer> PayerHistory { get; set; }
}

public class Recipient
{
    public Guid Id { get; set; }
}

public class Payer
{
    public Guid Id { get; set; }
}

EF映射类:

public class SubscriptionMapping : EntityTypeConfiguration<Subscription>
{
    public SubscriptionMapping()
    {
        ToTable("Subscriptions");
        HasKey(p => p.Id);

        Property(p => p.Id).HasColumnName("subscription_id");

        HasMany(p => p.RecipientHistory)
            .WithRequired()
            .Map(m => m.MapKey("subscription_id"));

        HasMany(p => p.PayerHistory)
            .WithRequired()
            .Map(m => m.MapKey("subscription_id"));
    }
}

public class RecipientMapping : EntityTypeConfiguration<Recipient>
{
    public RecipientMapping()
    {
        ToTable("Subscription_recipients");
        HasKey(p => p.Id);
        Property(p => p.Id).HasColumnName("subscription_recipient_id");
    }
}

public class PayerMapping : EntityTypeConfiguration<Payer>
{
    public PayerMapping()
    {
        ToTable("Subscription_payers");
        HasKey(p => p.Id);
        Property(p => p.Id).HasColumnName("subscription_payer_id");
    }
}

当我对商店执行以下查询时:

db.Set<Subscription>()
    .Include(s => s.RecipientHistory)
    .Include(s => s.PayerHistory)
    .ToList();

生成的SQL是:

SELECT 
    [UnionAll1].[C2] AS [C1], 
    [UnionAll1].[subscription_id] AS [C2], 
    [UnionAll1].[C1] AS [C3], 
    [UnionAll1].[C3] AS [C4], 
    [UnionAll1].[subscription_recipient_id] AS [C5], 
    [UnionAll1].[subscription_id1] AS [C6], 
    [UnionAll1].[C4] AS [C7], 
    [UnionAll1].[C5] AS [C8], 
    [UnionAll1].[C6] AS [C9]
    FROM  (SELECT 
        CASE WHEN ([Extent2].[subscription_recipient_id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], 
        1 AS [C2], 
        [Extent1].[subscription_id] AS [subscription_id], 
        CASE WHEN ([Extent2].[subscription_recipient_id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C3], 
        [Extent2].[subscription_recipient_id] AS [subscription_recipient_id], 
        [Extent2].[subscription_id] AS [subscription_id1], 
        CAST(NULL AS int) AS [C4], 
        CAST(NULL AS uniqueidentifier) AS [C5], 
        CAST(NULL AS uniqueidentifier) AS [C6]
        FROM  [dbo].[Subscriptions] AS [Extent1]
        LEFT OUTER JOIN [dbo].[Subscription_recipients] AS [Extent2] ON [Extent1].[subscription_id] = [Extent2].[subscription_id]
    UNION ALL
        SELECT 
        2 AS [C1], 
        2 AS [C2], 
        [Extent3].[subscription_id] AS [subscription_id], 
        CAST(NULL AS int) AS [C3], 
        CAST(NULL AS uniqueidentifier) AS [C4], 
        CAST(NULL AS uniqueidentifier) AS [C5], 
        2 AS [C6], 
        [Extent4].[subscription_payer_id] AS [subscription_payer_id], 
        [Extent4].[subscription_id] AS [subscription_id1]
        FROM  [dbo].[Subscriptions] AS [Extent3]
        INNER JOIN [dbo].[Subscription_payers] AS [Extent4] ON [Extent3].[subscription_id] = [Extent4].[subscription_id]) AS [UnionAll1]
    ORDER BY [UnionAll1].[subscription_id] ASC, [UnionAll1].[C1] ASC

对于这样一个简单的查询,这对我来说似乎没有必要。我在映射中遗漏了一些东西,还是这个?

1 个答案:

答案 0 :(得分:1)

渴望加载两个不同的依赖项是相当棘手的。这是一种安全的方法,尽可能使用尽可能少的空间 - 请注意,对于不是&#34;的所有行,结果为null。包括&#34;一部分。

基本问题是,如果使用两个1:*关联,则您无法将第一个关联的包含实体与第二个关联实体分开。当您处理1:(0-)1时,同样的问题不会出现,因为结果只是一条记录。

问题是,你正在考虑这个&#34;简单的1对多关系&#34;。如果您曾尝试在SQL中编写类似的查询,则必须知道1:*实际上非常复杂,特别是如果您需要在一个查询中组合多个不同的1:* s。现在,有其他选择(例如,在结果集中使用xml字段),但这可能是允许服务器尽可能多地进行优化的那个,并且开销实际上是在实践中相当小 - 再次注意,您只需获取每个数据一次 - 剩下的就是null s - 这就是第一个查询执行left join的原因(它即使关系中没有任何内容,也必须返回父级,而第二个可以自由地执行inner join(并且不必从父级中选择除主要内容之外的任何列键)。如果有办法不在第一个子查询的每一行中返回父数据,这仍然可以得到改善,但说起来容易做起来难(并且最终会慢得多)

总而言之,这是一个有点雄辩的查询,但它做了它必须做的事情。你有性能问题,还是只是好奇?