dotnet-dynamic-query/PoweredSoft.DynamicQuery/QueryHandler.cs

217 lines
8.5 KiB
C#
Raw Normal View History

2018-10-18 21:52:05 -04:00
using System;
2018-10-17 22:14:21 -04:00
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
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
{
2018-10-18 21:52:05 -04:00
internal MethodInfo ExecuteGeneric = typeof(QueryHandler).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).First(t => t.Name == "Execute" && t.IsGenericMethod);
internal IQueryExecutionResult ExecuteReflected() => (IQueryExecutionResult)ExecuteGeneric.MakeGenericMethod(QueryableUnderlyingType).Invoke(this, new object[]{});
2018-10-17 22:14:21 -04:00
2018-10-18 21:52:05 -04:00
protected virtual IQueryExecutionResult Execute<T>()
2018-10-17 22:14:21 -04:00
{
2018-10-18 21:52:05 -04:00
ApplyIncludeStrategyInterceptors<T>();
ApplyBeforeFilterInterceptors<T>();
2018-10-17 22:14:21 -04:00
ApplyFilters<T>();
2018-10-18 21:52:05 -04:00
return HasGrouping ? ExecuteGrouping<T>() : ExecuteNoGrouping<T>();
2018-10-17 22:14:21 -04:00
}
2018-10-18 21:52:05 -04:00
protected virtual IQueryExecutionResult ExecuteGrouping<T>()
2018-10-17 22:14:21 -04:00
{
var result = new QueryExecutionResult();
2018-10-21 17:06:45 -04:00
// preserve queryable.
var queryableAfterFilters = CurrentQueryable;
result.TotalRecords = queryableAfterFilters.LongCount();
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 :)
var finalGroups = Criteria.Groups.Select(g => InterceptGroup<T>(g)).ToList();
// get the aggregates.
2018-10-21 15:46:33 -04:00
var aggregateResults = Criteria.Aggregates.Any() ? FetchAggregates(finalGroups) : null;
2018-10-21 14:15:21 -04:00
// sorting.
2018-10-21 15:46:33 -04:00
finalGroups.ForEach(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.
2018-10-21 14:15:21 -04:00
ApplySorting<T>();
ApplyPaging<T>();
2018-10-21 15:46:33 -04:00
// create group & select expression.
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 =>
{
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.
var groupRecords = CurrentQueryable.ToDynamicClassList();
result.Data = groupRecords.Select((groupRecord, groupRecordIndex) =>
{
var groupRecordResult = new GroupQueryResult();
2018-10-21 15:46:33 -04:00
List<GroupQueryResult> previousGroupResults = new List<GroupQueryResult>();
List<IGroup> previousGroups = new List<IGroup>();
Criteria.Groups.ForEach((g, gi) =>
{
bool isFirst = gi == 0;
bool isLast = Criteria.Groups.Count - 1 == gi;
var cgrr = isFirst ? groupRecordResult : new GroupQueryResult();
cgrr.GroupPath = g.Path;
cgrr.GroupValue = groupRecord.GetDynamicPropertyValue($"Key_{gi}");
if (!isLast)
cgrr.Data = new List<object>();
else
cgrr.Data = groupRecord.GetDynamicPropertyValue<List<T>>("Records").Cast<object>().ToList();
2018-10-21 15:46:33 -04:00
if (previousGroupResults.Any())
previousGroupResults.Last().Data.Add(cgrr);
previousGroupResults.Add(cgrr);
previousGroups.Add(g);
2018-10-21 15:46:33 -04:00
// find aggregates for this group.
if (Criteria.Aggregates.Any())
{
var matchingAggregate = FindMatchingAggregateResult(aggregateResults, previousGroups, previousGroupResults);
cgrr.Aggregates = new List<IAggregateResult>();
2018-10-21 17:12:40 -04:00
Criteria.Aggregates.ForEach((a, ai) =>
2018-10-21 16:20:17 -04:00
{
2018-10-21 17:12:40 -04:00
var key = $"Agg_{ai}";
2018-10-21 16:20:17 -04:00
var aggregateResult = new AggregateResult
{
Path = a.Path,
Type = a.Type,
Value = matchingAggregate.GetDynamicPropertyValue(key)
};
cgrr.Aggregates.Add(aggregateResult);
});
2018-10-21 15:46:33 -04:00
}
});
return (object)groupRecordResult;
}).ToList();
2018-10-21 14:15:21 -04:00
2018-10-21 17:06:45 -04:00
result.Aggregates = CalculateTotalAggregate(queryableAfterFilters);
2018-10-19 17:44:13 -04:00
return result;
2018-10-17 22:14:21 -04:00
}
2018-10-21 17:06:45 -04:00
protected virtual List<IAggregateResult> CalculateTotalAggregate(IQueryable queryableAfterFilters)
{
if (!Criteria.Aggregates.Any())
return null;
var groupExpression = queryableAfterFilters.EmptyGroupBy(QueryableUnderlyingType);
var selectExpression = groupExpression.Select(sb =>
{
Criteria.Aggregates.ForEach((a, index) =>
{
var selectType = ResolveSelectFrom(a.Type);
sb.Aggregate(a.Path, selectType, $"Agg_{index}");
});
});
var aggregateResult = selectExpression.ToDynamicClassList().First();
var ret = new List<IAggregateResult>();
Criteria.Aggregates.ForEach((a, index) =>
{
ret.Add(new AggregateResult()
{
Path = a.Path,
Type = a.Type,
Value = aggregateResult.GetDynamicPropertyValue($"Agg_{index}")
});
});
return ret;
}
2018-10-21 15:46:33 -04:00
private DynamicClass FindMatchingAggregateResult(List<List<DynamicClass>> aggregateResults, List<IGroup> groups, List<GroupQueryResult> groupResults)
{
var groupIndex = groupResults.Count - 1;
var aggregateLevel = aggregateResults[groupIndex];
2018-10-19 17:44:13 -04:00
2018-10-21 15:46:33 -04:00
var ret = aggregateLevel.FirstOrDefault(al =>
{
for (var i = 0; i < groups.Count; i++)
{
if (!al.GetDynamicPropertyValue($"Key_{i}").Equals(groupResults[i].GroupValue))
return false;
}
return true;
});
return ret;
}
private List<List<DynamicClass>> FetchAggregates(List<IGroup> finalGroups)
{
List<List<DynamicClass>> aggregateResults;
var previousGroups = new List<IGroup>();
aggregateResults = finalGroups.Select(fg =>
{
var groupExpression = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb =>
{
var groupKeyIndex = -1;
previousGroups.ForEach(pg => gb.Path(pg.Path, $"Key_{++groupKeyIndex}"));
gb.Path(fg.Path, $"Key_{++groupKeyIndex}");
});
var selectExpression = groupExpression.Select(sb =>
{
var groupKeyIndex = -1;
previousGroups.ForEach(pg => sb.Key($"Key_{++groupKeyIndex}", $"Key_{groupKeyIndex}"));
sb.Key($"Key_{++groupKeyIndex}", $"Key_{groupKeyIndex}");
2018-10-21 17:12:40 -04:00
Criteria.Aggregates.ForEach((a, ai) =>
2018-10-21 15:46:33 -04:00
{
var selectType = ResolveSelectFrom(a.Type);
2018-10-21 17:12:40 -04:00
sb.Aggregate(a.Path, selectType, $"Agg_{ai}");
2018-10-21 15:46:33 -04:00
});
});
var aggregateResult = selectExpression.ToDynamicClassList();
previousGroups.Add(fg);
return aggregateResult;
}).ToList();
return aggregateResults;
}
2018-10-21 14:15:21 -04:00
2018-10-18 21:52:05 -04:00
protected virtual IQueryExecutionResult ExecuteNoGrouping<T>()
2018-10-17 22:14:21 -04:00
{
2018-10-18 21:52:05 -04:00
var result = new QueryExecutionResult();
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();
CalculatePageCount(result);
2018-10-17 22:14:21 -04:00
2018-10-18 21:52:05 -04:00
// sorts and paging.
2018-10-21 14:15:21 -04:00
ApplySorting<T>();
ApplyPaging<T>();
2018-10-18 21:52:05 -04:00
// the data.
result.Data = CurrentQueryable.ToObjectList();
2018-10-21 17:06:45 -04:00
result.Aggregates = CalculateTotalAggregate(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
}
2018-10-18 21:52:05 -04:00
public virtual IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria)
2018-10-17 22:14:21 -04:00
{
Reset(queryable, criteria);
2018-10-18 21:52:05 -04:00
return ExecuteReflected();
2018-10-17 22:14:21 -04:00
}
}
}