using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using PoweredSoft.DynamicLinq; using PoweredSoft.DynamicLinq.Fluent; using PoweredSoft.DynamicQuery.Core; namespace PoweredSoft.DynamicQuery { public class QueryHandler : QueryHandlerBase, IQueryHandler { public QueryHandler(IEnumerable queryableInterceptorProviders) : base(queryableInterceptorProviders) { } protected virtual IQueryExecutionResult FinalExecute() { CommonBeforeExecute(); return HasGrouping ? ExecuteGrouping() : ExecuteNoGrouping(); } protected virtual IQueryExecutionResult ExecuteGrouping() { var result = new QueryExecutionGroupResult(); // preserve queryable. var queryableAfterFilters = CurrentQueryable; result.TotalRecords = queryableAfterFilters.LongCount(); CalculatePageCount(result); // intercept groups in advance to avoid doing it more than once :) var finalGroups = Criteria.Groups.Select(g => InterceptGroup(g)).ToList(); // get the aggregates. var aggregateResults = FetchAggregates(finalGroups); // sorting. finalGroups.ReversedForEach(fg => Criteria.Sorts.Insert(0, new Sort(fg.Path, fg.Ascending))); // apply sorting and paging. ApplySorting(); ApplyPaging(); if (Options.GroupByInMemory) CurrentQueryable = CurrentQueryable.ToObjectList().Cast().AsQueryable(); CurrentQueryable = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb => { gb.NullChecking(Options.GroupByInMemory ? Options.GroupByInMemoryNullCheck : false); finalGroups.ForEach((fg, index) => gb.Path(fg.Path, $"Key_{index}")); }); CurrentQueryable = CurrentQueryable.Select(sb => { sb.NullChecking(Options.GroupByInMemory ? Options.GroupByInMemoryNullCheck : false); finalGroups.ForEach((fg, index) => sb.Key($"Key_{index}", $"Key_{index}")); sb.ToList("Records"); }); // loop through the grouped records. var groupRecords = CurrentQueryable.ToDynamicClassList(); // now join them into logical collections var lastLists = new List<(List source, IGroupQueryResult group)>(); result.Groups = RecursiveRegroup(groupRecords, aggregateResults, Criteria.Groups.First(), lastLists); // intercept grouped by. QueryInterceptToGrouped(lastLists).Wait(); result.Aggregates = CalculateTotalAggregate(queryableAfterFilters); return result; } protected virtual List CalculateTotalAggregate(IQueryable queryableAfterFilters) { if (!Criteria.Aggregates.Any()) return null; IQueryable selectExpression = CreateTotalAggregateSelectExpression(queryableAfterFilters); var aggregateResult = selectExpression.ToDynamicClassList().FirstOrDefault(); return MaterializeCalculateTotalAggregateResult(aggregateResult); } protected virtual List> FetchAggregates(List finalGroups) { if (!Criteria.Aggregates.Any()) return null; var previousGroups = new List(); var ret = finalGroups.Select(fg => { IQueryable selectExpression = CreateFetchAggregateSelectExpression(fg, previousGroups); var aggregateResult = selectExpression.ToDynamicClassList(); previousGroups.Add(fg); return aggregateResult; }).ToList(); return ret; } protected virtual IQueryExecutionResult ExecuteNoGrouping() { var result = new QueryExecutionResult(); // after filter queryable var afterFilterQueryable = CurrentQueryable; // total records. result.TotalRecords = afterFilterQueryable.LongCount(); CalculatePageCount(result); // sorts and paging. ApplySorting(); ApplyPaging(); // data. var entities = ((IQueryable)CurrentQueryable).ToList(); var records = InterceptConvertTo(entities).Result; result.Data = records; // aggregates. result.Aggregates = CalculateTotalAggregate(afterFilterQueryable); return result; } public IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria) { Reset(queryable, criteria, new QueryExecutionOptions()); return FinalExecute(); } public IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria) { Reset(queryable, criteria, new QueryExecutionOptions()); return FinalExecute(); } public IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria, IQueryExecutionOptions options) { Reset(queryable, criteria, options); return FinalExecute(); } public IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria, IQueryExecutionOptions options) { Reset(queryable, criteria, options); return FinalExecute(); } } }