c# - Entity Framework ,高效的 NavigationProperty.OfType 查询

标签 c# linq entity-framework

我在使用每表类型 (TPT) 继承在 EF4 中构建高效查询时遇到问题。

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

我试过:

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 中表达此查询的方法,它只涉及 Episode 和结果 SQL 中的 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%')
)

最佳答案

经过一番摸索之后,我终于成功了:

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 看起来很糟糕的导航属性所吸引。

关于c# - Entity Framework ,高效的 NavigationProperty.OfType 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6175616/

相关文章:

c# - 在 db - C# 中存储静态数据好吗?

c# - 在 C# 中使用泛型 - 从泛型类调用泛型类

c# - 使用 Linq 解析 XML 时,只会获取一个对象

c# - 查询在模拟 Dbcontext 上为 null 的实体关系

entity-framework - 映射到 View 的对象的意外迁移

c# - C# 中的自定义 javascript 反序列化器

c# - 在具有2个索引和2个别名(每个索引中有1个)的名称相同的Elasticsearch实例中,如何仅从一个索引中选择数据

c# - 您如何将此查询转换为方法语法?

c# - 如果第二个或第三个表为空,LINQ Join 不返回结果

linq - 如何通过 ASPnet Identity 中的 Linq 检索具有特定角色的用户