我目前正在尝试优化一些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;声明)?