.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; }
问题原因分析
- 先排序后分组失效:分组操作会重新组织数据结构,将原有序实体集合转换为组的集合,原排序上下文会被完全重置,导致之前的排序规则无法保留。
- 先分组后排序报错:当前使用动态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
相关产品推荐
相关产品推荐

