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;
|
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
|
|
|
|
{
|
2018-10-21 15:15:26 -04:00
|
|
|
|
var result = new QueryExecutionResult();
|
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 :)
|
|
|
|
|
var finalGroups = Criteria.Groups.Select(g => InterceptGroup<T>(g)).ToList();
|
|
|
|
|
|
|
|
|
|
// get the aggregates.
|
2018-10-21 17:37:59 -04:00
|
|
|
|
var aggregateResults = FetchAggregates<T>(finalGroups);
|
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.
|
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-11-15 20:42:30 -05:00
|
|
|
|
|
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
|
|
|
|
|
result.Data = RecursiveRegroup<T>(groupRecords, aggregateResults, Criteria.Groups.First());
|
|
|
|
|
|
|
|
|
|
/*
|
2018-10-21 15:15:26 -04:00
|
|
|
|
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>();
|
2018-10-21 15:15:26 -04:00
|
|
|
|
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)
|
2018-10-21 21:27:50 -04:00
|
|
|
|
{
|
2018-10-21 15:15:26 -04:00
|
|
|
|
cgrr.Data = new List<object>();
|
2018-10-21 21:27:50 -04:00
|
|
|
|
}
|
2018-10-21 15:15:26 -04:00
|
|
|
|
else
|
2018-10-21 21:27:50 -04:00
|
|
|
|
{
|
|
|
|
|
var entities = groupRecord.GetDynamicPropertyValue<List<T>>("Records");
|
|
|
|
|
var records = InterceptConvertTo<T>(entities);
|
|
|
|
|
cgrr.Data = records;
|
|
|
|
|
}
|
2018-10-21 15:15:26 -04:00
|
|
|
|
|
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:15:26 -04:00
|
|
|
|
|
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
|
|
|
|
}
|
2018-10-21 15:15:26 -04:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return (object)groupRecordResult;
|
2018-11-15 20:42:30 -05:00
|
|
|
|
}).ToList();*/
|
2018-10-21 14:15:21 -04:00
|
|
|
|
|
2018-10-21 17:37:59 -04:00
|
|
|
|
result.Aggregates = CalculateTotalAggregate<T>(queryableAfterFilters);
|
2018-10-19 17:44:13 -04:00
|
|
|
|
return result;
|
2018-10-17 22:14:21 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-15 20:42:30 -05:00
|
|
|
|
protected virtual List<object> RecursiveRegroup<T>(List<DynamicClass> groupRecords, List<List<DynamicClass>> aggregateResults, IGroup group, List<IGroupQueryResult> parentGroupResults = null)
|
|
|
|
|
{
|
|
|
|
|
var groupIndex = Criteria.Groups.IndexOf(group);
|
|
|
|
|
var isLast = Criteria.Groups.Last() == group;
|
|
|
|
|
var groups = Criteria.Groups.Take(groupIndex + 1).ToList();
|
|
|
|
|
var hasAggregates = Criteria.Aggregates.Any();
|
|
|
|
|
|
|
|
|
|
var ret = groupRecords
|
|
|
|
|
.GroupBy(gk => gk.GetDynamicPropertyValue($"Key_{groupIndex}"))
|
|
|
|
|
.Select(t =>
|
|
|
|
|
{
|
|
|
|
|
var groupResult = new GroupQueryResult();
|
|
|
|
|
|
|
|
|
|
// group results.
|
|
|
|
|
|
|
|
|
|
List<IGroupQueryResult> groupResults;
|
|
|
|
|
if (parentGroupResults == null)
|
|
|
|
|
groupResults = new List<IGroupQueryResult> { groupResult };
|
|
|
|
|
else
|
|
|
|
|
groupResults = parentGroupResults.Union(new[] { groupResult }).ToList();
|
|
|
|
|
|
|
|
|
|
groupResult.GroupPath = group.Path;
|
|
|
|
|
groupResult.GroupValue = t.Key;
|
|
|
|
|
|
|
|
|
|
if (hasAggregates)
|
|
|
|
|
{
|
|
|
|
|
var matchingAggregate = FindMatchingAggregateResult(aggregateResults, groups, groupResults);
|
|
|
|
|
if (matchingAggregate == null)
|
|
|
|
|
Debugger.Break();
|
|
|
|
|
|
|
|
|
|
groupResult.Aggregates = new List<IAggregateResult>();
|
|
|
|
|
Criteria.Aggregates.ForEach((a, ai) =>
|
|
|
|
|
{
|
|
|
|
|
var key = $"Agg_{ai}";
|
|
|
|
|
var aggregateResult = new AggregateResult
|
|
|
|
|
{
|
|
|
|
|
Path = a.Path,
|
|
|
|
|
Type = a.Type,
|
|
|
|
|
Value = matchingAggregate.GetDynamicPropertyValue(key)
|
|
|
|
|
};
|
|
|
|
|
groupResult.Aggregates.Add(aggregateResult);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isLast)
|
|
|
|
|
{
|
|
|
|
|
var entities = t.SelectMany(t2 => t2.GetDynamicPropertyValue<List<T>>("Records")).ToList();
|
|
|
|
|
groupResult.Data = InterceptConvertTo<T>(entities);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
groupResult.Data = RecursiveRegroup<T>(t.ToList(), aggregateResults, Criteria.Groups[groupIndex+1], groupResults);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return groupResult;
|
|
|
|
|
})
|
|
|
|
|
.AsEnumerable<object>()
|
|
|
|
|
.ToList();
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-21 17:37:59 -04:00
|
|
|
|
protected virtual List<IAggregateResult> CalculateTotalAggregate<T>(IQueryable queryableAfterFilters)
|
2018-10-21 17:06:45 -04:00
|
|
|
|
{
|
|
|
|
|
if (!Criteria.Aggregates.Any())
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
var groupExpression = queryableAfterFilters.EmptyGroupBy(QueryableUnderlyingType);
|
|
|
|
|
var selectExpression = groupExpression.Select(sb =>
|
|
|
|
|
{
|
|
|
|
|
Criteria.Aggregates.ForEach((a, index) =>
|
|
|
|
|
{
|
2018-10-21 17:37:59 -04:00
|
|
|
|
var fa = InterceptAggregate<T>(a);
|
|
|
|
|
var selectType = ResolveSelectFrom(fa.Type);
|
|
|
|
|
sb.Aggregate(fa.Path, selectType, $"Agg_{index}");
|
2018-10-21 17:06:45 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2018-11-15 20:42:30 -05:00
|
|
|
|
var aggregateResult = selectExpression.ToDynamicClassList().FirstOrDefault();
|
2018-10-21 17:06:45 -04:00
|
|
|
|
var ret = new List<IAggregateResult>();
|
|
|
|
|
Criteria.Aggregates.ForEach((a, index) =>
|
|
|
|
|
{
|
|
|
|
|
ret.Add(new AggregateResult()
|
|
|
|
|
{
|
|
|
|
|
Path = a.Path,
|
|
|
|
|
Type = a.Type,
|
2018-11-15 20:42:30 -05:00
|
|
|
|
Value = aggregateResult?.GetDynamicPropertyValue($"Agg_{index}")
|
2018-10-21 17:06:45 -04:00
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-15 20:42:30 -05:00
|
|
|
|
private DynamicClass FindMatchingAggregateResult(List<List<DynamicClass>> aggregateResults, List<IGroup> groups, List<IGroupQueryResult> groupResults)
|
2018-10-21 15:46:33 -04:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-10-21 17:37:59 -04:00
|
|
|
|
private List<List<DynamicClass>> FetchAggregates<T>(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
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
{
|
2018-10-21 17:37:59 -04:00
|
|
|
|
var fa = InterceptAggregate<T>(a);
|
|
|
|
|
var selectType = ResolveSelectFrom(fa.Type);
|
|
|
|
|
sb.Aggregate(fa.Path, selectType, $"Agg_{ai}");
|
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
|
|
|
|
|
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();
|
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.
|
2018-10-21 14:15:21 -04:00
|
|
|
|
ApplySorting<T>();
|
|
|
|
|
ApplyPaging<T>();
|
2018-10-21 21:27:50 -04:00
|
|
|
|
|
|
|
|
|
// data.
|
|
|
|
|
var entities = ((IQueryable<T>)CurrentQueryable).ToList();
|
|
|
|
|
var records = InterceptConvertTo<T>(entities);
|
|
|
|
|
result.Data = records;
|
|
|
|
|
|
|
|
|
|
// aggregates.
|
2018-10-21 17:37:59 -04:00
|
|
|
|
result.Aggregates = CalculateTotalAggregate<T>(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
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|