2018-10-18 21:52:05 -04:00
|
|
|
|
using System;
|
2018-10-17 22:14:21 -04:00
|
|
|
|
using System.Collections.Generic;
|
2018-11-15 20:42:30 -05:00
|
|
|
|
using System.Diagnostics;
|
2018-10-17 22:14:21 -04:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Text;
|
2019-03-19 23:54:15 -04:00
|
|
|
|
using System.Threading;
|
2019-02-23 23:47:50 -05:00
|
|
|
|
using System.Threading.Tasks;
|
2018-10-18 21:52:05 -04:00
|
|
|
|
using PoweredSoft.DynamicLinq;
|
2018-10-21 14:15:21 -04:00
|
|
|
|
using PoweredSoft.DynamicLinq.Fluent;
|
2018-10-18 21:52:05 -04:00
|
|
|
|
using PoweredSoft.DynamicQuery.Core;
|
2018-10-17 22:14:21 -04:00
|
|
|
|
|
|
|
|
|
namespace PoweredSoft.DynamicQuery
|
|
|
|
|
{
|
2018-10-18 21:52:05 -04:00
|
|
|
|
public class QueryHandler : QueryHandlerBase, IQueryHandler
|
2018-10-17 22:14:21 -04:00
|
|
|
|
{
|
2019-03-19 23:54:15 -04:00
|
|
|
|
protected virtual IQueryExecutionResult<TRecord> FinalExecute<TSource, TRecord>()
|
2018-10-17 22:14:21 -04:00
|
|
|
|
{
|
2019-03-19 23:54:15 -04:00
|
|
|
|
CommonBeforeExecute<TSource>();
|
|
|
|
|
return HasGrouping ? ExecuteGrouping<TSource, TRecord>() : ExecuteNoGrouping<TSource, TRecord>();
|
2018-10-17 22:14:21 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-19 23:54:15 -04:00
|
|
|
|
protected virtual IQueryExecutionResult<TRecord> ExecuteGrouping<TSource, TRecord>()
|
2018-10-17 22:14:21 -04:00
|
|
|
|
{
|
2019-03-22 16:27:04 -04:00
|
|
|
|
var result = new QueryExecutionGroupResult<TRecord>();
|
2018-10-21 17:06:45 -04:00
|
|
|
|
|
|
|
|
|
// preserve queryable.
|
|
|
|
|
var queryableAfterFilters = CurrentQueryable;
|
|
|
|
|
|
|
|
|
|
result.TotalRecords = queryableAfterFilters.LongCount();
|
2018-10-21 15:15:26 -04:00
|
|
|
|
CalculatePageCount(result);
|
2018-10-19 17:44:13 -04:00
|
|
|
|
|
2018-10-21 14:15:21 -04:00
|
|
|
|
// intercept groups in advance to avoid doing it more than once :)
|
2019-03-19 23:54:15 -04:00
|
|
|
|
var finalGroups = Criteria.Groups.Select(g => InterceptGroup<TSource>(g)).ToList();
|
2018-10-21 14:15:21 -04:00
|
|
|
|
|
|
|
|
|
// get the aggregates.
|
2019-03-19 23:54:15 -04:00
|
|
|
|
var aggregateResults = FetchAggregates<TSource>(finalGroups);
|
2018-10-21 14:15:21 -04:00
|
|
|
|
|
|
|
|
|
// sorting.
|
2019-04-22 21:18:38 -04:00
|
|
|
|
finalGroups.ReversedForEach(fg => Criteria.Sorts.Insert(0, new Sort(fg.Path, fg.Ascending)));
|
2018-10-19 17:44:13 -04:00
|
|
|
|
|
2018-10-21 15:46:33 -04:00
|
|
|
|
// apply sorting and paging.
|
2019-03-19 23:54:15 -04:00
|
|
|
|
ApplySorting<TSource>();
|
|
|
|
|
ApplyPaging<TSource>();
|
2018-10-21 14:15:21 -04:00
|
|
|
|
|
2018-10-21 15:46:33 -04:00
|
|
|
|
// create group & select expression.
|
2018-10-21 14:23:23 -04:00
|
|
|
|
CurrentQueryable = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb => finalGroups.ForEach((fg, index) => gb.Path(fg.Path, $"Key_{index}")));
|
2018-10-21 14:15:21 -04:00
|
|
|
|
CurrentQueryable = CurrentQueryable.Select(sb =>
|
|
|
|
|
{
|
2018-10-21 15:15:26 -04:00
|
|
|
|
finalGroups.ForEach((fg, index) => sb.Key($"Key_{index}", $"Key_{index}"));
|
2018-10-21 14:15:21 -04:00
|
|
|
|
sb.ToList("Records");
|
|
|
|
|
});
|
|
|
|
|
|
2018-10-21 15:46:33 -04:00
|
|
|
|
// loop through the grouped records.
|
2018-10-21 15:15:26 -04:00
|
|
|
|
var groupRecords = CurrentQueryable.ToDynamicClassList();
|
2018-11-15 20:42:30 -05:00
|
|
|
|
|
|
|
|
|
// now join them into logical collections
|
2019-03-19 23:54:15 -04:00
|
|
|
|
var lastLists = new List<(List<TSource> source, IGroupQueryResult<TRecord> group)>();
|
|
|
|
|
result.Groups = RecursiveRegroup<TSource, TRecord>(groupRecords, aggregateResults, Criteria.Groups.First(), lastLists);
|
2019-02-23 23:47:50 -05:00
|
|
|
|
|
|
|
|
|
// intercept grouped by.
|
2019-03-19 23:54:15 -04:00
|
|
|
|
QueryInterceptToGrouped<TSource, TRecord>(lastLists).Wait();
|
2018-11-15 20:42:30 -05:00
|
|
|
|
|
2019-03-19 23:54:15 -04:00
|
|
|
|
result.Aggregates = CalculateTotalAggregate<TSource>(queryableAfterFilters);
|
2018-10-19 17:44:13 -04:00
|
|
|
|
return result;
|
2018-10-17 22:14:21 -04:00
|
|
|
|
}
|
2019-03-19 23:54:15 -04:00
|
|
|
|
protected virtual List<IAggregateResult> CalculateTotalAggregate<TSource>(IQueryable queryableAfterFilters)
|
2018-10-21 17:06:45 -04:00
|
|
|
|
{
|
|
|
|
|
if (!Criteria.Aggregates.Any())
|
|
|
|
|
return null;
|
|
|
|
|
|
2019-03-19 23:54:15 -04:00
|
|
|
|
IQueryable selectExpression = CreateTotalAggregateSelectExpression<TSource>(queryableAfterFilters);
|
2018-11-15 20:42:30 -05:00
|
|
|
|
var aggregateResult = selectExpression.ToDynamicClassList().FirstOrDefault();
|
2018-12-06 22:24:03 -05:00
|
|
|
|
return MaterializeCalculateTotalAggregateResult(aggregateResult);
|
2018-10-21 17:06:45 -04:00
|
|
|
|
}
|
2018-12-06 22:24:03 -05:00
|
|
|
|
|
2019-03-19 23:54:15 -04:00
|
|
|
|
protected virtual List<List<DynamicClass>> FetchAggregates<TSource>(List<IGroup> finalGroups)
|
2018-10-21 15:46:33 -04:00
|
|
|
|
{
|
2018-10-21 17:37:59 -04:00
|
|
|
|
if (!Criteria.Aggregates.Any())
|
|
|
|
|
return null;
|
2018-10-21 21:34:53 -04:00
|
|
|
|
|
2018-10-21 15:46:33 -04:00
|
|
|
|
var previousGroups = new List<IGroup>();
|
2018-10-21 21:34:53 -04:00
|
|
|
|
var ret = finalGroups.Select(fg =>
|
2018-10-21 15:46:33 -04:00
|
|
|
|
{
|
2019-03-19 23:54:15 -04:00
|
|
|
|
IQueryable selectExpression = CreateFetchAggregateSelectExpression<TSource>(fg, previousGroups);
|
2018-10-21 15:46:33 -04:00
|
|
|
|
var aggregateResult = selectExpression.ToDynamicClassList();
|
|
|
|
|
previousGroups.Add(fg);
|
|
|
|
|
return aggregateResult;
|
|
|
|
|
}).ToList();
|
2018-10-21 21:34:53 -04:00
|
|
|
|
return ret;
|
2018-10-21 15:46:33 -04:00
|
|
|
|
}
|
2018-10-21 14:15:21 -04:00
|
|
|
|
|
2019-03-19 23:54:15 -04:00
|
|
|
|
protected virtual IQueryExecutionResult<TRecord> ExecuteNoGrouping<TSource, TRecord>()
|
2018-10-17 22:14:21 -04:00
|
|
|
|
{
|
2019-03-19 23:54:15 -04:00
|
|
|
|
var result = new QueryExecutionResult<TRecord>();
|
2018-10-17 22:14:21 -04:00
|
|
|
|
|
2018-10-21 17:06:45 -04:00
|
|
|
|
// after filter queryable
|
|
|
|
|
var afterFilterQueryable = CurrentQueryable;
|
|
|
|
|
|
2018-10-18 21:52:05 -04:00
|
|
|
|
// total records.
|
2018-10-21 17:06:45 -04:00
|
|
|
|
result.TotalRecords = afterFilterQueryable.LongCount();
|
2018-10-21 15:15:26 -04:00
|
|
|
|
CalculatePageCount(result);
|
2018-10-17 22:14:21 -04:00
|
|
|
|
|
2018-10-18 21:52:05 -04:00
|
|
|
|
// sorts and paging.
|
2019-03-19 23:54:15 -04:00
|
|
|
|
ApplySorting<TSource>();
|
|
|
|
|
ApplyPaging<TSource>();
|
2018-10-21 21:27:50 -04:00
|
|
|
|
|
|
|
|
|
// data.
|
2019-03-19 23:54:15 -04:00
|
|
|
|
var entities = ((IQueryable<TSource>)CurrentQueryable).ToList();
|
|
|
|
|
var records = InterceptConvertTo<TSource, TRecord>(entities).Result;
|
2018-10-21 21:27:50 -04:00
|
|
|
|
result.Data = records;
|
|
|
|
|
|
|
|
|
|
// aggregates.
|
2019-03-19 23:54:15 -04:00
|
|
|
|
result.Aggregates = CalculateTotalAggregate<TSource>(afterFilterQueryable);
|
2018-10-17 22:14:21 -04:00
|
|
|
|
|
2018-10-18 21:52:05 -04:00
|
|
|
|
return result;
|
2018-10-17 22:14:21 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-19 23:54:15 -04:00
|
|
|
|
public IQueryExecutionResult<TSource> Execute<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria)
|
|
|
|
|
{
|
|
|
|
|
Reset(queryable, criteria);
|
|
|
|
|
return FinalExecute<TSource, TSource>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IQueryExecutionResult<TRecord> Execute<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria)
|
2018-10-17 22:14:21 -04:00
|
|
|
|
{
|
|
|
|
|
Reset(queryable, criteria);
|
2019-03-19 23:54:15 -04:00
|
|
|
|
return FinalExecute<TSource, TRecord>();
|
2018-10-17 22:14:21 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|