实体框架,高效的NavigationProperty.OfType查询

问题描述

|| 我在使用每表类型(TPT)继承在EF4中构造高效查询时遇到麻烦。 我有一个名为“情节”的实体,每个情节可以有多个事件。这里有几种不同类型的事件,它们均来自称为事件的基本实体。我想过滤所有不包含特定事件类型的剧集。剧集具有一个导航属性,该属性是其所有事件的集合(即基本事件类型的集合) 我试过了:
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扩展,该扩展会查询每个事件类型表。 难道没有一种方法可以在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 [C2],cast(1 as bit) AS [C6]
                        FROM [dbo].[InvasivePostTreatmentEvent] AS [Extent5]) AS [UnionAll1]
                UNION ALL
                    SELECT 
                    [Extent6].[Id] AS [Id],cast(1 as bit) AS [C3],cast(0 as bit) AS [C6]
                    FROM [dbo].[InvasiveTreatmentEvent] AS [Extent6]) AS [UnionAll2]
            UNION ALL
                SELECT 
                [Extent7].[Id] AS [Id],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 [C6]
            FROM [dbo].[InvasiveMOXFQEvent] AS [Extent8]) AS [UnionAll4]
    UNION ALL
        SELECT 
        [Extent9].[Id] AS [Id],cast(1 as bit) AS [C4],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<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。     ,        .Any()比.Count()更好 根据我的预期,您的查询可以优化为:
context.EpisodeSet
.Where(e => e.Events.Any(p => p is DerivedEvent))
.Select(e => e);