entity framework,高效的NavigationProperty.OfType查询

我在使用每表类型(TPT)inheritance在EF4中构建有效查询时遇到了麻烦。

我有一个名为Episode的实体,每集都可以有多个事件。 有几种不同类型的事件都来自名为Event的基本实体。 我想过滤所有不包含某种类型事件的剧集。 Episode有一个导航属性,它是所有事件的集合(即基本事件类型的集合)

我试过了:

from episode in context.EpisodeSet where episode.Events.OfType().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中EpisodeDerivedEvent表之间的连接吗?

编辑:回应教授X这里是生成的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%') ) 

经过多次努力,我已经设法让这个工作:

 var episodes = (from episode in context.EpisodeSet join e in context.EventSet.OfType() on episode.Id equals e.EpisodeId into outer from o in outer.DefaultIfEmpty() where o == null select episode) 

因此,我不必尝试将OfTypefilter应用于导航属性,而是将其应用于ObjectSet并执行外连接。 似乎OfType和’as’类型过滤对导航属性不起作用。

这会生成DerivedEvent表中没有相应事件的剧集,以及您手工编写的SQL类型。

LINQ遵循您使用SQL自然编写查询的方式。 它很容易被所有这些导航属性所诱惑,导致看起来很漂亮的LINQ但看起来很糟糕的SQL。

.Any()优于.Count()从我的角度来看,您的查询可以优化为:

 context.EpisodeSet .Where(e => e.Events.Any(p => p is DerivedEvent)) .Select(e => e);