简体   繁体   English

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

[英]Entity Framework, efficient NavigationProperty.OfType query

I'm having trouble constructing an efficient query in EF4 using type per table (TPT) inheritance.我在使用每表类型 (TPT) inheritance 在 EF4 中构建高效查询时遇到问题。

I have an entity called Episode , and each episode can have multiple events.我有一个名为Episode的实体,每一集都可以有多个事件。 There several different type of events all deriving from a base entity called Event .有几种不同类型的事件都源自一个名为Event的基本实体。 I want to filter on all episodes that don't contain a certain type of event.我想过滤所有不包含特定类型事件的剧集。 Episode has a navigation property that is a collection of all its events (ie a collection of the base Event type) Episode有一个导航属性,它是所有事件的集合(即基本事件类型的集合)

I've tried:我试过了:

from episode in context.EpisodeSet
where episode.Events.OfType<DerivedEvent>().Count() == 0
select episode

and

from episode in context.EpisodeSet
where episode.Events.Where(p => p is DerivedEvent).Count() == 0
select episode

Both of these produce a typical long SQL expansion that queries every Event type table.这两者都产生了一个典型的长 SQL 扩展,用于查询每个事件类型表。

Shouldn't there be a way to express this query in LINQ that just involves a join between the Episode and the DerivedEvent table in the resulting SQL?难道不应该有一种方法可以在 LINQ 中表达这个查询,它只涉及生成的 SQL 中的EpisodeDerivedEvent表之间的连接吗?

Edit: In response to ProfessorX here is the generated SQL (basically just a typical massive union across all event tables)编辑:作为对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%')
)

After much head scratching I've managed to get this to work:经过多次挠头后,我设法让它工作:

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)

So rather than try to apply an OfType filter to the navigation property I've had to apply it to the ObjectSet and do an outer join.因此,与其尝试将 OfType 过滤器应用于导航属性,不如将其应用于 ObjectSet 并进行外部连接。 Seems that OfType and 'as' type filtering don't work against Navigation Properties.似乎 OfType 和“as”类型过滤不适用于导航属性。

This produces the episodes that don't have a corresponding event in the DerivedEvent table, and with the kind of SQL that you would write by hand.这会生成在 DerivedEvent 表中没有相应事件的剧集,并且使用您将手写的 SQL 类型。

The LINQ follows the way you'd naturally write the query with SQL. LINQ 遵循您使用 SQL 自然编写查询的方式。 It's just too easy to get seduced by all these navigation properties that lead to nice looking LINQ but awful looking SQL.很容易被所有这些导航属性所吸引,这些导航属性导致外观漂亮的 LINQ 但看起来很糟糕的 SQL。

.Any() is better than.Count() From my prospective your query can be optimized to: .Any() 优于 .Count() 从我的预期来看,您的查询可以优化为:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM