子查询,内部选择,不将Order By应用于基础查询

时间:2017-12-12 19:03:11

标签: c# entity-framework linq-to-sql

在下面的linq语句中(在.net 4.5中)我试图在两个表上进行左连接,然后根据在第二个表上完成的排序,只取每个连接的第一行。

using (var context = new TestEntities())
{
    var items = ctx.FirstTables
                   .GroupJoin(
                       ctx.SecondTables,
                       first => first.IntJoin,
                       second => second.IntJoin,
                       (firstTableRow, secondTableRows) 
                           => new { firstTableRow, secondTableRows })
                   .Select(
                       result => new
                                     {
                                         result.firstTableRow.IntJoin,
                                         NewWanted = result.secondTableRows
                                                        .OrderByDescending(x => x.Sort)
                                                        .Select(x => (int?)x.Wanted)
                                                        .DefaultIfEmpty(null)
                                                        .FirstOrDefault()
                                     });
}

但是OrderByDescending调用未应用于第二个表,因此Wanted列始终返回数据库中第一列的值。

因此给出了下表值

FirstTable:

IntJoin
0
1
2
null

SecondTable:

IntJoin || Sort || Wanted
0          0       1
0          1       2
0          2       3
1          0       6
1          1       5
1          2       4
null       0       7
null       1       9
null       2       8

我希望得到以下列表:

IntJoin || NewWanted
0          3
1          4
2          null
null       8

然而,我得到的是

IntJoin || NewWanted
0          1
1          6
2          null
null       7

在.net Core 2.0.1

中运行的EntityFrameworkCore中获取以下内容
IntJoin || NewWanted
0          3
1          4
2          null
null       null

因此.net Framework中的实体框架忽略了OrderByDescending调用而实体框架核心不是(.net Core似乎无法处理null比较。我没有使用核心而只是创建一个测试差异的例子,这个问题最好留给(或可能在另一个问题中回答)

我已经在.net 4.5和4.7(使用Entity Framework 6.2.0),.net Core 2.0(使用Entity Framework Core 2.0.1)和Microsoft Sql Server 2008和2016中尝试了所有这些并获得了相同的结果。

我知道我可能会在.ToList()GroupJoin之间调用Select,但这是较大查询的一部分,在内存中执行查询会非常令人望而却步。

我也尝试调用OrderBy以及不同的值,包括Sort中的Select列没有任何差异

1 个答案:

答案 0 :(得分:2)

由于生成的SQL查询没有ORDER BY子句,因此看起来你已经击中(少数几个与EF Core相比)EF6查询转换错误:

SELECT
    1 AS [C1],
    [Project3].[IntJoin] AS [IntJoin],
    [Project3].[C1] AS [C2]
    FROM ( SELECT
        [Extent1].[IntJoin] AS [IntJoin],
        (SELECT TOP (1)
            [Project1].[Wanted] AS [Wanted]
            FROM   ( SELECT 1 AS X ) AS [SingleRowTable1]
            LEFT OUTER JOIN  (SELECT
                [Extent2].[Wanted] AS [Wanted]
                FROM [dbo].[SecondTable] AS [Extent2]
                WHERE ([Extent1].[IntJoin] = [Extent2].[IntJoin]) OR (([Extent1].[IntJoin] IS NULL) AND ([Extent2].[IntJoin] IS NULL)) ) AS [Project1] ON 1 = 1) AS [C1]
        FROM [dbo].[FirstTable] AS [Extent1]
    )  AS [Project3]

解决方案是在DefaultIfEmpty之前移动OrderByDescending(基本上遵循“常规”LINQ左外连接模式:

NewWanted = result.secondTableRows
                .DefaultIfEmpty() // <--
                .OrderByDescending(x => x.Sort)
                .Select(x => (int?)x.Wanted)
                //.DefaultIfEmpty()
                .FirstOrDefault()

转换为:

SELECT
    1 AS [C1],
    [Extent1].[IntJoin] AS [IntJoin],
    [Limit1].[Wanted] AS [Wanted]
    FROM  [dbo].[FirstTable] AS [Extent1]
    OUTER APPLY  (SELECT TOP (1) [Project2].[Wanted] AS [Wanted]
        FROM ( SELECT
            [Project1].[Sort] AS [Sort],
            [Project1].[Wanted] AS [Wanted]
            FROM   ( SELECT 1 AS X ) AS [SingleRowTable1]
            LEFT OUTER JOIN  (SELECT
                [Extent2].[Sort] AS [Sort],
                [Extent2].[Wanted] AS [Wanted]
                FROM [dbo].[SecondTable] AS [Extent2]
                WHERE ([Extent1].[IntJoin] = [Extent2].[IntJoin]) OR (([Extent1].[IntJoin] IS NULL) AND ([Extent2].[IntJoin] IS NULL)) ) AS [Project1] ON 1 = 1
        )  AS [Project2]
        ORDER BY [Project2].[Sort] DESC ) AS [Limit1]

并产生所需的结果。