在不返回大型匿名类型集的情况下优化LINQ到实体

时间:2014-07-29 10:18:22

标签: c# sql entity-framework linq-to-entities query-optimization

我目前正在尝试优化一些LINQ to Entity查询(EF 6.1.1),以便生成的SQL更有效。我设法重构了我的语句,因此SQL几乎就是我写它的方式,但是我遇到了来自查询的返回对象的问题。

一个相对简单的例子就是我最初使用lambda语法的地方:

var originalQuery = context.WorkstationConfigurations
                           .Include(config => config.WorkstationOptions)
                           .Include(config => config.WorkstationOptions.Select(option => option.ImageWorkstationOption))
                           .Include(config => config.WorkstationOptions.Select(option => option.ImageWorkstationOption).Select(iwo => iwo.Image))
                           .Include(config => config.WorkstationPowerBarTileAssignments);
List<WorkstationConfiguration> originalResult = originalQuery.ToList();

这正确地返回了3个WorkstationConfiguration entites的列表,对象图都正确连接,但它产生了非常可怕的SQL,有多个选择,联合,按顺序等等:

SELECT 
[UnionAll1].[RowVersion] AS [C1], 
[UnionAll1].[WorkstationConfigurationID] AS [C2], 
[UnionAll1].[Name] AS [C3], 
...
...
FROM  (SELECT 
    CASE WHEN ([Join2].[Name1] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1], 
    [Extent1].[RowVersion] AS [RowVersion], 
    [Extent1].[WorkstationConfigurationID] AS [WorkstationConfigurationID], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[ConfigurationType] AS [ConfigurationType], 
    [Extent1].[RowVersion] AS [RowVersion1], 
    [Join2].[Name1] AS [Name1], 
    ...
    ...
    FROM  [dbo].[WorkstationConfiguration] AS [Extent1]
    LEFT OUTER JOIN  (SELECT [Extent2].[Name] AS [Name1], [Extent2].[LineNumber] AS [LineNumber1], [Extent2].[WorkstationConfigurationID] AS [WorkstationConfigurationID1], [Extent2].[TypeCode] AS [TypeCode], [Extent2].[RowVersion] AS [RowVersion1], [Extent3].[Name] AS [Name2], [Extent3].[LineNumber] AS [LineNumber2], [Extent3].[WorkstationConfigurationID] AS [WorkstationConfigurationID2], [Extent3].[ImageID] AS [ImageID1], [Extent3].[RowVersion] AS [RowVersion2], [Extent4].[ImageID] AS [ImageID2], [Extent4].[ImageData] AS [ImageData], [Extent4].[SizeInBytes] AS [SizeInBytes], [Extent4].[WidthInPixels] AS [WidthInPixels], [Extent4].[HeightInPixels] AS [HeightInPixels], [Extent4].[ImageHash] AS [ImageHash], [Extent4].[Description] AS [Description], [Extent4].[ImageTypeID] AS [ImageTypeID], [Extent4].[ContentType] AS [ContentType], [Extent4].[ThumbnailImageData] AS [ThumbnailImageData], [Extent4].[RowVersion] AS [RowVersion3]
        FROM   [dbo].[WorkstationOption] AS [Extent2]
        LEFT OUTER JOIN [dbo].[ImageWorkstationOption] AS [Extent3] ON ([Extent2].[WorkstationConfigurationID] = [Extent3].[WorkstationConfigurationID]) AND ([Extent2].[LineNumber] = [Extent3].[LineNumber]) AND ([Extent2].[Name] = [Extent3].[Name])
        LEFT OUTER JOIN [dbo].[Image] AS [Extent4] ON [Extent3].[ImageID] = [Extent4].[ImageID] ) AS [Join2] ON [Extent1].[WorkstationConfigurationID] = [Join2].[WorkstationConfigurationID1]
UNION ALL
    SELECT 
    2 AS [C1], 
    [Extent5].[RowVersion] AS [RowVersion], 
    [Extent5].[WorkstationConfigurationID] AS [WorkstationConfigurationID], 
    [Extent5].[Name] AS [Name], 
    [Extent5].[ConfigurationType] AS [ConfigurationType], 
    [Extent5].[RowVersion] AS [RowVersion1], 
    CAST(NULL AS int) AS [C2], 
    CAST(NULL AS int) AS [C3], 
    CAST(NULL AS int) AS [C4], 
    ...
    ...
    FROM  [dbo].[WorkstationConfiguration] AS [Extent5]
    INNER JOIN [dbo].[WorkstationPowerBarTileAssignment] AS [Extent6] ON [Extent5].[WorkstationConfigurationID] = [Extent6].[WorkstationConfigurationID]) AS [UnionAll1]
ORDER BY [UnionAll1].[WorkstationConfigurationID] ASC, [UnionAll1].[C1] ASC

所以经过一些谷歌搜索后,我发现查询语法对于多个连接/复杂查询更好,所以我重构了这个:

var optimisedQuery = from workstationConfiguration in context.WorkstationConfigurations
                     from workstationOption in workstationConfiguration.WorkstationOptions
                     from tileAssignment in workstationConfiguration.WorkstationPowerBarTileAssignments.DefaultIfEmpty()
                     select new
                            {
                              a = workstationConfiguration,
                              b = workstationOption,
                              c = workstationOption.ImageWorkstationOption,
                              d = workstationOption.ImageWorkstationOption.Image,
                              e = tileAssignment
                            };
var optimisedResult = optimisedQuery.ToList();

这产生了非常完美的SQL(单选,正确的连接),就像我手写的那样我会写的:

SELECT 
[Extent1].[RowVersion] AS [RowVersion], 
[Extent1].[WorkstationConfigurationID] AS [WorkstationConfigurationID], 
[Extent1].[Name] AS [Name], 
[Extent1].[ConfigurationType] AS [ConfigurationType], 
[Extent2].[Name] AS [Name1], 
...
...
FROM [dbo].[WorkstationConfiguration] AS [Extent1]
INNER JOIN [dbo].[WorkstationOption] AS [Extent2] ON [Extent1].[WorkstationConfigurationID] = [Extent2].[WorkstationConfigurationID]
LEFT OUTER JOIN [dbo].[WorkstationPowerBarTileAssignment] AS [Extent3] ON [Extent1].[WorkstationConfigurationID] = [Extent3].[WorkstationConfigurationID]
LEFT OUTER JOIN [dbo].[ImageWorkstationOption] AS [Extent4] ON ([Extent2].[Name] = [Extent4].[Name]) AND ([Extent2].[LineNumber] = [Extent4].[LineNumber]) AND ([Extent2].[WorkstationConfigurationID] = [Extent4].[WorkstationConfigurationID])
LEFT OUTER JOIN [dbo].[Image] AS [Extent5] ON [Extent4].[ImageID] = [Extent5].[ImageID]

实体框架负责正确填充对象图(正确连接所有对象和导航属性),但我最终为数据库返回的每一行(316)提供一个匿名类型条目,而不是一组3不同的WorkstationConfiguration对象。因此,如果我从结果集中手动提取唯一的WorkstationConfiguration对象(&#39; a&#39;),那么我会得到相同的最终结果,但是使用优化的SQL。

有没有办法可以获得优化的SQL,所有填充的子实体/对象图都会自动连接,而不会获得多余的匿名类型(即从我优化的LINQ中直接返回一个List&lt; WorkstationConfiguration&gt;声明)?

0 个答案:

没有答案