protected virtual async Task <IQueryExecutionResult> ExecuteAsyncGrouping <T>(CancellationToken cancellationToken) { var result = new QueryExecutionResult(); // preserve queryable. var queryableAfterFilters = CurrentQueryable; // async. result.TotalRecords = await this.AsyncQueryableService.LongCountAsync((IQueryable <T>) queryableAfterFilters, cancellationToken); CalculatePageCount(result); // intercept groups in advance to avoid doing it more than once :) var finalGroups = Criteria.Groups.Select(g => InterceptGroup <T>(g)).ToList(); // get the aggregates. var aggregateResults = await FetchAggregatesAsync <T>(finalGroups, cancellationToken); // sorting. finalGroups.ForEach(fg => Criteria.Sorts.Insert(0, new Sort(fg.Path, fg.Ascending))); // apply sorting and paging. ApplySorting <T>(); ApplyPaging <T>(); // create group & select expression. CurrentQueryable = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb => finalGroups.ForEach((fg, index) => gb.Path(fg.Path, $"Key_{index}"))); CurrentQueryable = CurrentQueryable.Select(sb => { finalGroups.ForEach((fg, index) => sb.Key($"Key_{index}", $"Key_{index}")); sb.ToList("Records"); }); // loop through the grouped records. // var groupRecords = await AsyncQueryableService.ToListAsync(CurrentQueryable.Cast<DynamicClass>(), cancellationToken); // now join them into logical collections // result.Data = RecursiveRegroup<T>(groupRecords, aggregateResults, Criteria.Groups.First()); result.Data = CurrentQueryable; result.Aggregates = await CalculateTotalAggregateAsync <T>(queryableAfterFilters, cancellationToken); return(result); }