我试图调试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
对于这样一个简单的查询,这对我来说似乎没有必要。我在映射中遗漏了一些东西,还是这个?
答案 0 :(得分:1)
渴望加载两个不同的依赖项是相当棘手的。这是一种安全的方法,尽可能使用尽可能少的空间 - 请注意,对于不是&#34;的所有行,结果为null
。包括&#34;一部分。
基本问题是,如果使用两个1:*关联,则您无法将第一个关联的包含实体与第二个关联实体分开。当您处理1:(0-)1时,同样的问题不会出现,因为结果只是一条记录。
问题是,你正在考虑这个&#34;简单的1对多关系&#34;。如果您曾尝试在SQL中编写类似的查询,则必须知道1:*实际上非常复杂,特别是如果您需要在一个查询中组合多个不同的1:* s。现在,有其他选择(例如,在结果集中使用xml
字段),但这可能是允许服务器尽可能多地进行优化的那个,并且开销实际上是在实践中相当小 - 再次注意,您只需获取每个数据一次 - 剩下的就是null
s - 这就是第一个查询执行left join
的原因(它即使关系中没有任何内容,也必须返回父级,而第二个可以自由地执行inner join
(并且不必从父级中选择除主要内容之外的任何列键)。如果有办法不在第一个子查询的每一行中返回父数据,这仍然可以得到改善,但说起来容易做起来难(并且最终会慢得多)
总而言之,这是一个有点雄辩的查询,但它做了它必须做的事情。你有性能问题,还是只是好奇?