You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 8中Dynamic LINQ分组后排序失效及报错问题求助

.NET 8通用可过滤仓储分组后排序异常问题解决

我们在.NET 8中实现了一个通用可过滤仓储,支持接收过滤条件列表与排序规则列表,可适配任意实体。但针对Task实体,业务要求每次过滤必须分组,这带来两个棘手问题:

  • 先排序再分组:之前设置的排序规则会直接失效
  • 先分组再排序:抛出错误提示 The given key 'EmptyProjectionMember' was not present in the dictionary

相关代码

public static (IQueryable Queryable, int TotalElements) BuildFilteredQuery(IQueryable queryable, List<FilterData> filters, List<SortData> sorts, int pageNo, int pageSize, bool distinct)
{
    queryable = ApplyFilters(queryable, filters);
    queryable = ApplySorting(queryable, sorts);

    if (distinct)
    {
        queryable = queryable.GroupBy("new(CountSort)").Select("it.First()");
    }

    var totalElements = queryable.Count();

    if (pageSize != 0)
    {
        queryable = ApplyPaging(queryable, pageNo, pageSize);
    }

    return (queryable, totalElements);
}

private static IQueryable ApplySorting(IQueryable queryable, List<SortData> sorts)
{
    var sortingString = string.Join(", ", sorts.Select(s =>
        $"{s.PropertyName} {(s.Direction == ListSortDirection.Ascending ? "asc" : "desc")}"));

    if (!string.IsNullOrWhiteSpace(sortingString))
    {
        queryable = queryable.OrderBy(sortingString);
    }

    return queryable;
}

问题原因分析

  1. 先排序后分组失效:分组操作会重新组织数据结构,将原有序实体集合转换为组的集合,原排序上下文会被完全重置,导致之前的排序规则无法保留。
  2. 先分组后排序报错:当前使用动态Linq的GroupBy("new(CountSort)").Select("it.First()")语法,分组后通过it.First()获取的实体,动态Linq无法正确映射原实体的排序属性,分组操作改变了查询的投影结构,导致排序时找不到对应的成员。

解决方案

方案一:调整执行顺序,分组后重新应用排序

先执行过滤,再分组,最后重新应用排序规则,避免分组重置排序上下文:

public static (IQueryable Queryable, int TotalElements) BuildFilteredQuery(IQueryable queryable, List<FilterData> filters, List<SortData> sorts, int pageNo, int pageSize, bool distinct)
{
    queryable = ApplyFilters(queryable, filters);

    // 标记是否需要排序
    var needSort = sorts != null && sorts.Any();

    if (distinct)
    {
        queryable = queryable.GroupBy("new(CountSort)").Select("it.First()");
    }

    // 分组后重新应用排序
    if (needSort)
    {
        queryable = ApplySorting(queryable, sorts);
    }

    var totalElements = queryable.Count();

    if (pageSize != 0)
    {
        queryable = ApplyPaging(queryable, pageNo, pageSize);
    }

    return (queryable, totalElements);
}

// ApplySorting方法保持不变
private static IQueryable ApplySorting(IQueryable queryable, List<SortData> sorts)
{
    var sortingString = string.Join(", ", sorts.Select(s =>
        $"{s.PropertyName} {(s.Direction == ListSortDirection.Ascending ? "asc" : "desc")}"));

    if (!string.IsNullOrWhiteSpace(sortingString))
    {
        queryable = queryable.OrderBy(sortingString);
    }

    return queryable;
}

方案二:改用强类型分组(针对Task实体特殊处理)

如果动态Linq的投影问题无法解决,可以针对Task实体做强类型分组,避免动态语法的映射错误:

public static (IQueryable Queryable, int TotalElements) BuildFilteredQuery(IQueryable queryable, List<FilterData> filters, List<SortData> sorts, int pageNo, int pageSize, bool distinct)
{
    queryable = ApplyFilters(queryable, filters);

    if (distinct)
    {
        // 针对Task实体使用强类型分组
        if (queryable.ElementType == typeof(Task))
        {
            var taskQuery = queryable.Cast<Task>();
            queryable = taskQuery.GroupBy(t => t.CountSort)
                                 .Select(g => g.First())
                                 .AsQueryable();
        }
        else
        {
            // 其他实体保留原动态分组逻辑
            queryable = queryable.GroupBy("new(CountSort)").Select("it.First()");
        }
    }

    queryable = ApplySorting(queryable, sorts);

    var totalElements = queryable.Count();

    if (pageSize != 0)
    {
        queryable = ApplyPaging(queryable, pageNo, pageSize);
    }

    return (queryable, totalElements);
}

内容的提问来源于stack exchange,提问作者Predrag Despotovic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 23:15:12