我在使用每表类型(TPT)继承在EF4中构建高效查询时遇到了麻烦。
我有一个名为剧集的实体,每集都可以有多个事件。有几种不同类型的事件都来自名为事件的基本实体。我想过滤所有不包含某种类型事件的剧集。 Episode 有一个导航属性,它是所有事件的集合(即基本事件类型的集合)
我试过了:
from episode in context.EpisodeSet
where episode.Events.OfType<DerivedEvent>().Count() == 0
select episode
和
from episode in context.EpisodeSet
where episode.Events.Where(p => p is DerivedEvent).Count() == 0
select episode
这两者都产生了一个典型的长SQL扩展,可以查询每个 Event 类型表。
在LINQ中是否应该有一种表达此查询的方法只涉及生成的SQL中 Episode 和 DerivedEvent 表之间的连接?
修改 响应于ProfessorX,这里生成的SQL(基本上只是所有事件表中的典型大规模联合)
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[TypeId] AS [TypeId],
[Extent1].[PatientId] AS [PatientId],
[Extent1].[CentreId] AS [CentreId],
[Extent1].[CreatedOn] AS [CreatedOn],
[Extent1].[UpdatedOn] AS [UpdatedOn],
[Extent1].[CreatedBy] AS [CreatedBy],
[Extent1].[UpdatedBy] AS [UpdatedBy]
FROM [dbo].[Episode] AS [Extent1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Event] AS [Extent2]
LEFT OUTER JOIN (SELECT
[Extent3].[Id] AS [Id],
cast(1 as bit) AS [C1]
FROM [dbo].[InvasiveDischargableEvent] AS [Extent3] ) AS [Project1] ON [Extent2].[Id] = [Project1].[Id]
LEFT OUTER JOIN (SELECT
[UnionAll4].[C1] AS [C1],
[UnionAll4].[C2] AS [C2],
[UnionAll4].[C3] AS [C3],
[UnionAll4].[C4] AS [C4],
[UnionAll4].[C5] AS [C5],
[UnionAll4].[C6] AS [C6],
[UnionAll4].[C7] AS [C7]
FROM (SELECT
[UnionAll3].[C1] AS [C1],
[UnionAll3].[C2] AS [C2],
[UnionAll3].[C3] AS [C3],
[UnionAll3].[C4] AS [C4],
[UnionAll3].[C5] AS [C5],
[UnionAll3].[C6] AS [C6],
[UnionAll3].[C7] AS [C7]
FROM (SELECT
[UnionAll2].[C1] AS [C1],
[UnionAll2].[C2] AS [C2],
[UnionAll2].[C3] AS [C3],
[UnionAll2].[C4] AS [C4],
[UnionAll2].[C5] AS [C5],
[UnionAll2].[C6] AS [C6],
[UnionAll2].[C7] AS [C7]
FROM (SELECT
[UnionAll1].[Id] AS [C1],
[UnionAll1].[C1] AS [C2],
[UnionAll1].[C2] AS [C3],
[UnionAll1].[C3] AS [C4],
[UnionAll1].[C4] AS [C5],
[UnionAll1].[C5] AS [C6],
[UnionAll1].[C6] AS [C7]
FROM (SELECT
[Extent4].[Id] AS [Id],
cast(0 as bit) AS [C1],
cast(1 as bit) AS [C2],
cast(0 as bit) AS [C3],
cast(0 as bit) AS [C4],
cast(0 as bit) AS [C5],
cast(0 as bit) AS [C6]
FROM [dbo].[InvasivePSQ10Event] AS [Extent4]
UNION ALL
SELECT
[Extent5].[Id] AS [Id],
cast(0 as bit) AS [C1],
cast(0 as bit) AS [C2],
cast(0 as bit) AS [C3],
cast(0 as bit) AS [C4],
cast(0 as bit) AS [C5],
cast(1 as bit) AS [C6]
FROM [dbo].[InvasivePostTreatmentEvent] AS [Extent5]) AS [UnionAll1]
UNION ALL
SELECT
[Extent6].[Id] AS [Id],
cast(0 as bit) AS [C1],
cast(0 as bit) AS [C2],
cast(1 as bit) AS [C3],
cast(0 as bit) AS [C4],
cast(0 as bit) AS [C5],
cast(0 as bit) AS [C6]
FROM [dbo].[InvasiveTreatmentEvent] AS [Extent6]) AS [UnionAll2]
UNION ALL
SELECT
[Extent7].[Id] AS [Id],
cast(0 as bit) AS [C1],
cast(0 as bit) AS [C2],
cast(0 as bit) AS [C3],
cast(0 as bit) AS [C4],
cast(1 as bit) AS [C5],
cast(0 as bit) AS [C6]
FROM [dbo].[InvasiveConsultationEvent] AS [Extent7]) AS [UnionAll3]
UNION ALL
SELECT
[Extent8].[Id] AS [Id],
cast(1 as bit) AS [C1],
cast(0 as bit) AS [C2],
cast(0 as bit) AS [C3],
cast(0 as bit) AS [C4],
cast(0 as bit) AS [C5],
cast(0 as bit) AS [C6]
FROM [dbo].[InvasiveMOXFQEvent] AS [Extent8]) AS [UnionAll4]
UNION ALL
SELECT
[Extent9].[Id] AS [Id],
cast(0 as bit) AS [C1],
cast(0 as bit) AS [C2],
cast(0 as bit) AS [C3],
cast(1 as bit) AS [C4],
cast(0 as bit) AS [C5],
cast(0 as bit) AS [C6]
FROM [dbo].[InvasiveReferralEvent] AS [Extent9]) AS [UnionAll5] ON [Extent2].[Id] = [UnionAll5].[C1]
WHERE ([Extent1].[Id] = [Extent2].[EpisodeId]) AND (CASE WHEN (( NOT (([UnionAll5].[C2] = 1) AND ([UnionAll5].[C2] IS NOT NULL))) AND ( NOT (([UnionAll5].[C3] = 1) AND ([UnionAll5].[C3] IS NOT NULL))) AND ( NOT (([UnionAll5].[C4] = 1) AND ([UnionAll5].[C4] IS NOT NULL))) AND ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL))) AND ( NOT (([UnionAll5].[C5] = 1) AND ([UnionAll5].[C5] IS NOT NULL)))) THEN '2X' WHEN (([UnionAll5].[C5] = 1) AND ([UnionAll5].[C5] IS NOT NULL)) THEN '2X0X' WHEN (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL) AND ( NOT (([UnionAll5].[C6] = 1) AND ([UnionAll5].[C6] IS NOT NULL))) AND ( NOT (([UnionAll5].[C7] = 1) AND ([UnionAll5].[C7] IS NOT NULL)))) THEN '2X1X' WHEN (([UnionAll5].[C4] = 1) AND ([UnionAll5].[C4] IS NOT NULL)) THEN '2X2X' WHEN (([UnionAll5].[C2] = 1) AND ([UnionAll5].[C2] IS NOT NULL)) THEN '2X3X' WHEN (([UnionAll5].[C6] = 1) AND ([UnionAll5].[C6] IS NOT NULL)) THEN '2X1X0X' WHEN (([UnionAll5].[C7] = 1) AND ([UnionAll5].[C7] IS NOT NULL)) THEN '2X1X1X' ELSE '2X4X' END LIKE '2X4X%')
)
答案 0 :(得分:4)
经过多次努力,我已经成功实现了这一目标:
var episodes = (from episode in context.EpisodeSet
join e in context.EventSet.OfType<DerivedEvent>() on episode.Id equals e.EpisodeId into outer
from o in outer.DefaultIfEmpty()
where o == null
select episode)
因此,我不必尝试将OfType过滤器应用于导航属性,而是将其应用于ObjectSet并执行外连接。似乎OfType和'as'类型过滤对导航属性不起作用。
这会产生DerivedEvent表中没有相应事件的剧集,以及您手工编写的SQL类型。
LINQ遵循您使用SQL自然编写查询的方式。很容易被所有这些导航属性所诱惑,导致看起来很漂亮的LINQ但看起来很糟糕的SQL。
答案 1 :(得分:2)
.Any()优于.Count() 从我的角度来看,您的查询可以优化为:
context.EpisodeSet
.Where(e => e.Events.Any(p => p is DerivedEvent))
.Select(e => e);