【问题标题】:Why is EF Core adding an extra ORDER BY on the end为什么 EF Core 最后要添加一个额外的 ORDER BY
【发布时间】:2021-08-04 18:18:50
【问题描述】:

我有一个问题:

    public async Task<IEnumerable<CategoryDto>> GetCategoriesAsync()
    {
        var dto = await _context.Categories.FromSqlRaw(
                @"SELECT * FROM [cp].[GetCategoryTree]")
            .ProjectTo<CategoryDto>(_mapper.ConfigurationProvider)
            .AsNoTrackingWithIdentityResolution()
            .ToListAsync();

        return dto;
    }

它产生这个 sql:

SELECT [c].[banner_group_id], [c].[category_id], [c].[description], [c].[inactive], [c].[issitecategory], [c].[keywords], [c].[category_name], [c].[page_content], [c].[parent_category_id], [c].[sort_order], [c].[title], [b].[banner_group_id], [b].[category_id], [b].[description], [b].[inactive], [b].[issitecategory], [b].[keywords], [b].[category_name], [b].[page_content], [b].[parent_category_id], [b].[sort_order], [b].[title]

FROM (

    SELECT * FROM [cp].[GetCategoryTree]

) AS [c]

LEFT JOIN [bma_ec_categories] AS [b] ON [c].[category_id] = [b].[parent_category_id]

ORDER BY [c].[category_id], [b].[category_id]

[cp].[GetCategoryTree] 的输出已在该视图中排序。为什么 EF Core 最后要添加一个额外的ORDER BY?我可以告诉 EF Core 不排序吗?

【问题讨论】:

  • 奇怪...是 Automapper 的 ProjectTo 吗?您可以删除它并记录您的查询吗?
  • @GHDevOps 是的,就是这么做的。
  • 映射到CategoryDto 导致加入,我看到当 EF-core 加入时它总是按 PK 值添加排序,可能是为了使后处理更容易(在内存中构建对象)。
  • 视图没有排序,什么意思它已经排序了?
  • 不,他们不能,这只是一个能够将单词ORDER BY 放在视图中的黑客,这仅意味着TOP 在排序后被评估,它没有 我重复 DOES NOT 对外部查询强制执行任何排序,这是一个完整的神话。见stackoverflow.com/a/15188437/14868997mssqltips.com/sqlservertip/4488/…bengribaudo.com/blog/2015/05/01/3430/…dba.stackexchange.com/a/21437/220697

标签: c# entity-framework-core


【解决方案1】:

递归CTE返回结果是普通的记录集,也就是说如果你在客户端需要树,你必须自己重构它。

让我们重用原始查询:

public async Task<IEnumerable<CategoryDto>> GetCategoriesAsync()
{
    var plainResult = await _context.Categories.FromSqlRaw(@"SELECT * FROM [cp].[GetCategoryTree]")
        .Select(c => new CategoryDto
        {
            CategoryId = c.CategoryId,
            ParentId = c.ParentId,
            Name = c.Name,
            SortOrder = c.SortOrder
        })
        .ToListAsync();

       var lookup = plainResult.Where(x => x.ParentId != 0).ToLookup(x => x.ParentId);

       foreach (c in plainResult)
       {
           if (lookup.ContainsKey(c.CategoryId))
           {
               c.Children = lookup[c.CategoryId]
                   .OrderBy(x => x.SortOrder)
                   .ThenBy(x => x.Name)
                   .ToList();
           }            
       }

    var dto = plainResult.Where(x => x.ParentId == 0).ToList();

    return dto;
}

【讨论】:

  • 其实我在这里看不到为什么需要递归 CTE。您可以直接从_context.Categories 进行原始查询。如果要选择树的叶子,则需要 CTE。
【解决方案2】:

我从一个平面 Category 查询开始。我需要它在树结构中。所以我为Children 添加了一个Include。但它只包括根元素上的子元素。我问了 SO here 并被指向 Recursive CTE。这就是[cp].[GetCategoryTree]。我现在已经放弃了所有这些并提出了我的问题:

    public async Task<IEnumerable<CategoryDto>> GetCategoriesAsync()
    {
       var dto = await _context.Categories
            .Where(x => x.ParentId == 0)
            .OrderBy(x=> x.SortOrder)
            .ThenBy(x => x.Name)
            .ProjectTo<CategoryDto>(_mapper.ConfigurationProvider)
            .AsNoTracking()
            .ToListAsync();

       foreach (var categoryDto in dto)
       {
           foreach (var categoryDtoChild in categoryDto.Children)
           {
               categoryDtoChild.Children = await _context.Categories
                   .Where(x => x.ParentId == categoryDtoChild.CategoryId)
                   .OrderBy(x => x.SortOrder)
                   .ThenBy(x => x.Name)
                   .ProjectTo<CategoryDto>(_mapper.ConfigurationProvider)
                   .AsNoTracking()
                   .ToListAsync();
            }
       }

        return dto;
    }

这很有效,慢慢确定,但一直填充到曾孙。如果有人有更好的解决方案,我会全力以赴。

基准测试结果(我的和@SvyatoslavDanyliv):

Method IterationCount Mean Error StdDev Median Min Max Ratio RatioSD Gen 0 Gen 1 Gen 2 Allocated
GetCategories 1 142.82 ms 5.260 ms 14.921 ms 142.65 ms 85.572 ms 170.29 ms 1.00 0.00 1000.0000 - - 5,845 KB
GetCategoriesSingleQuery 1 11.17 ms 0.501 ms 1.396 ms 10.87 ms 8.057 ms 14.76 ms 0.08 0.01 - - - 914 KB
GetCategories 2 290.03 ms 7.703 ms 21.473 ms 288.38 ms 239.879 ms 350.48 ms 1.00 0.00 2000.0000 - - 11,637 KB
GetCategoriesSingleQuery 2 23.21 ms 1.815 ms 5.207 ms 21.58 ms 17.005 ms 38.73 ms 0.08 0.02 - - - 1,745 KB

我想我会接受@SvyatoslavDanyliv 的回答。

【讨论】:

  • 为什么不单查询?
  • @SvyatoslavDanyliv 请给我一个单个查询的示例。
  • 我已经添加了另一个答案。
猜你喜欢
  • 2022-01-16
  • 2022-01-06
  • 2018-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-07
  • 1970-01-01
  • 2019-05-13
相关资源
最近更新 更多