Async handler is ready just need to be tested.
This commit is contained in:
parent
24a18e065b
commit
23b45a00ac
@ -19,7 +19,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicQuery.Test", "PoweredSoft.DynamicQuery.Test\PoweredSoft.DynamicQuery.Test.csproj", "{3EAD8217-8E10-4261-9055-50444905922C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.DynamicQuery.AspNetCore", "PoweredSoft.DynamicQuery.AspNetCore\PoweredSoft.DynamicQuery.AspNetCore.csproj", "{DF58BD18-AB47-4018-B1EA-D1118D93B408}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicQuery.AspNetCore", "PoweredSoft.DynamicQuery.AspNetCore\PoweredSoft.DynamicQuery.AspNetCore.csproj", "{DF58BD18-AB47-4018-B1EA-D1118D93B408}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.Data.Core", "..\Data\PoweredSoft.Data.Core\PoweredSoft.Data.Core.csproj", "{E3CBE47B-08CF-4EAC-A1DF-312E78EAA141}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.Data.EntityFrameworkCore", "..\Data\PoweredSoft.Data.EntityFrameworkCore\PoweredSoft.Data.EntityFrameworkCore.csproj", "{9D3ED339-8AF2-44B7-9AC5-720A0AC6C2FD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
@ -51,6 +55,14 @@ Global
|
||||
{DF58BD18-AB47-4018-B1EA-D1118D93B408}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DF58BD18-AB47-4018-B1EA-D1118D93B408}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DF58BD18-AB47-4018-B1EA-D1118D93B408}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E3CBE47B-08CF-4EAC-A1DF-312E78EAA141}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E3CBE47B-08CF-4EAC-A1DF-312E78EAA141}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E3CBE47B-08CF-4EAC-A1DF-312E78EAA141}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E3CBE47B-08CF-4EAC-A1DF-312E78EAA141}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{9D3ED339-8AF2-44B7-9AC5-720A0AC6C2FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{9D3ED339-8AF2-44B7-9AC5-720A0AC6C2FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{9D3ED339-8AF2-44B7-9AC5-720A0AC6C2FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{9D3ED339-8AF2-44B7-9AC5-720A0AC6C2FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PoweredSoft.DynamicQuery.Core
|
||||
@ -15,8 +16,8 @@ namespace PoweredSoft.DynamicQuery.Core
|
||||
IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria);
|
||||
}
|
||||
|
||||
public interface IAsyncQueryHandler : IInterceptableQueryHandler
|
||||
public interface IQueryHandlerAsync : IInterceptableQueryHandler
|
||||
{
|
||||
Task<IQueryExecutionResult> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria);
|
||||
Task<IQueryExecutionResult> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken));
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Data\PoweredSoft.Data.Core\PoweredSoft.Data.Core.csproj" />
|
||||
<ProjectReference Include="..\..\DynamicLinq\PoweredSoft.DynamicLinq\PoweredSoft.DynamicLinq.csproj" />
|
||||
<ProjectReference Include="..\PoweredSoft.DynamicQuery.Core\PoweredSoft.DynamicQuery.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -13,16 +13,15 @@ namespace PoweredSoft.DynamicQuery
|
||||
public class QueryHandler : QueryHandlerBase, IQueryHandler
|
||||
{
|
||||
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[]{});
|
||||
internal IQueryExecutionResult ExecuteReflected() => (IQueryExecutionResult)ExecuteGeneric.MakeGenericMethod(QueryableUnderlyingType).Invoke(this, new object[] { });
|
||||
|
||||
protected virtual IQueryExecutionResult Execute<T>()
|
||||
{
|
||||
ApplyIncludeStrategyInterceptors<T>();
|
||||
ApplyBeforeFilterInterceptors<T>();
|
||||
ApplyFilters<T>();
|
||||
CommonBeforeExecute<T>();
|
||||
return HasGrouping ? ExecuteGrouping<T>() : ExecuteNoGrouping<T>();
|
||||
}
|
||||
|
||||
|
||||
protected virtual IQueryExecutionResult ExecuteGrouping<T>()
|
||||
{
|
||||
var result = new QueryExecutionResult();
|
||||
@ -54,181 +53,28 @@ namespace PoweredSoft.DynamicQuery
|
||||
sb.ToList("Records");
|
||||
});
|
||||
|
||||
|
||||
// loop through the grouped records.
|
||||
var groupRecords = CurrentQueryable.ToDynamicClassList();
|
||||
|
||||
// now join them into logical collections
|
||||
result.Data = RecursiveRegroup<T>(groupRecords, aggregateResults, Criteria.Groups.First());
|
||||
|
||||
/*
|
||||
result.Data = groupRecords.Select((groupRecord, groupRecordIndex) =>
|
||||
{
|
||||
var groupRecordResult = new GroupQueryResult();
|
||||
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
|
||||
{
|
||||
var entities = groupRecord.GetDynamicPropertyValue<List<T>>("Records");
|
||||
var records = InterceptConvertTo<T>(entities);
|
||||
cgrr.Data = records;
|
||||
}
|
||||
|
||||
if (previousGroupResults.Any())
|
||||
previousGroupResults.Last().Data.Add(cgrr);
|
||||
|
||||
previousGroupResults.Add(cgrr);
|
||||
previousGroups.Add(g);
|
||||
|
||||
// find aggregates for this group.
|
||||
if (Criteria.Aggregates.Any())
|
||||
{
|
||||
var matchingAggregate = FindMatchingAggregateResult(aggregateResults, previousGroups, previousGroupResults);
|
||||
cgrr.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)
|
||||
};
|
||||
cgrr.Aggregates.Add(aggregateResult);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return (object)groupRecordResult;
|
||||
}).ToList();*/
|
||||
|
||||
result.Aggregates = CalculateTotalAggregate<T>(queryableAfterFilters);
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
protected virtual List<IAggregateResult> CalculateTotalAggregate<T>(IQueryable queryableAfterFilters)
|
||||
{
|
||||
if (!Criteria.Aggregates.Any())
|
||||
return null;
|
||||
|
||||
var groupExpression = queryableAfterFilters.EmptyGroupBy(QueryableUnderlyingType);
|
||||
var selectExpression = groupExpression.Select(sb =>
|
||||
{
|
||||
Criteria.Aggregates.ForEach((a, index) =>
|
||||
{
|
||||
var fa = InterceptAggregate<T>(a);
|
||||
var selectType = ResolveSelectFrom(fa.Type);
|
||||
sb.Aggregate(fa.Path, selectType, $"Agg_{index}");
|
||||
});
|
||||
});
|
||||
|
||||
IQueryable selectExpression = CreateTotalAggregateSelectExpression<T>(queryableAfterFilters);
|
||||
var aggregateResult = selectExpression.ToDynamicClassList().FirstOrDefault();
|
||||
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;
|
||||
return MaterializeCalculateTotalAggregateResult(aggregateResult);
|
||||
}
|
||||
|
||||
private DynamicClass FindMatchingAggregateResult(List<List<DynamicClass>> aggregateResults, List<IGroup> groups, List<IGroupQueryResult> groupResults)
|
||||
{
|
||||
var groupIndex = groupResults.Count - 1;
|
||||
var aggregateLevel = aggregateResults[groupIndex];
|
||||
|
||||
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<T>(List<IGroup> finalGroups)
|
||||
|
||||
protected virtual List<List<DynamicClass>> FetchAggregates<T>(List<IGroup> finalGroups)
|
||||
{
|
||||
if (!Criteria.Aggregates.Any())
|
||||
return null;
|
||||
@ -236,26 +82,7 @@ namespace PoweredSoft.DynamicQuery
|
||||
var previousGroups = new List<IGroup>();
|
||||
var ret = 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}");
|
||||
Criteria.Aggregates.ForEach((a, ai) =>
|
||||
{
|
||||
var fa = InterceptAggregate<T>(a);
|
||||
var selectType = ResolveSelectFrom(fa.Type);
|
||||
sb.Aggregate(fa.Path, selectType, $"Agg_{ai}");
|
||||
});
|
||||
});
|
||||
|
||||
IQueryable selectExpression = CreateFetchAggregateSelectExpression<T>(fg, previousGroups);
|
||||
var aggregateResult = selectExpression.ToDynamicClassList();
|
||||
previousGroups.Add(fg);
|
||||
return aggregateResult;
|
||||
@ -288,6 +115,7 @@ namespace PoweredSoft.DynamicQuery
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public virtual IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria)
|
||||
{
|
||||
|
132
PoweredSoft.DynamicQuery/QueryHandlerAsync.cs
Normal file
132
PoweredSoft.DynamicQuery/QueryHandlerAsync.cs
Normal file
@ -0,0 +1,132 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using PoweredSoft.Data.Core;
|
||||
using PoweredSoft.DynamicLinq;
|
||||
using PoweredSoft.DynamicQuery.Core;
|
||||
|
||||
namespace PoweredSoft.DynamicQuery
|
||||
{
|
||||
public class QueryHandlerAsync : QueryHandlerBase, IQueryHandlerAsync
|
||||
{
|
||||
public IAsyncQueryableFactory AsyncQueryableFactory { get; }
|
||||
internal MethodInfo ExecuteAsyncGeneric = typeof(QueryHandler).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).First(t => t.Name == "ExecuteAsync" && t.IsGenericMethod);
|
||||
internal Task<IQueryExecutionResult> ExecuteAsyncReflected(CancellationToken cancellationToken) => (Task<IQueryExecutionResult>)ExecuteAsyncGeneric.MakeGenericMethod(QueryableUnderlyingType).Invoke(this, new object[] { cancellationToken });
|
||||
|
||||
public QueryHandlerAsync(IAsyncQueryableFactory asyncQueryableFactory)
|
||||
{
|
||||
AsyncQueryableFactory = asyncQueryableFactory;
|
||||
}
|
||||
|
||||
public Task<IQueryExecutionResult> ExecuteAsync<T>(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
CommonBeforeExecute<T>();
|
||||
return HasGrouping ? ExecuteAsyncGrouping<T>(cancellationToken) : ExecuteAsyncNoGrouping<T>(cancellationToken);
|
||||
}
|
||||
|
||||
public Task<IQueryExecutionResult> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
Reset(queryable, criteria);
|
||||
return ExecuteAsyncReflected(cancellationToken);
|
||||
}
|
||||
|
||||
protected virtual async Task<IQueryExecutionResult> ExecuteAsyncGrouping<T>(CancellationToken cancellationToken)
|
||||
{
|
||||
var result = new QueryExecutionResult();
|
||||
|
||||
// preserve queryable.
|
||||
var queryableAfterFilters = CurrentQueryable;
|
||||
|
||||
// async.
|
||||
result.TotalRecords = await this.AsyncQueryableFactory.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 AsyncQueryableFactory.ToListAsync(CurrentQueryable.Cast<DynamicClass>(), cancellationToken);
|
||||
|
||||
// now join them into logical collections
|
||||
result.Data = RecursiveRegroup<T>(groupRecords, aggregateResults, Criteria.Groups.First());
|
||||
|
||||
result.Aggregates = await CalculateTotalAggregateAsync<T>(queryableAfterFilters, cancellationToken);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected async Task<IQueryExecutionResult> ExecuteAsyncNoGrouping<T>(CancellationToken cancellationToken)
|
||||
{
|
||||
var result = new QueryExecutionResult();
|
||||
|
||||
// after filter queryable
|
||||
IQueryable<T> afterFilterQueryable = (IQueryable<T>)CurrentQueryable;
|
||||
|
||||
// total records.
|
||||
result.TotalRecords = await AsyncQueryableFactory.LongCountAsync(afterFilterQueryable, cancellationToken);
|
||||
CalculatePageCount(result);
|
||||
|
||||
// sorts and paging.
|
||||
ApplySorting<T>();
|
||||
ApplyPaging<T>();
|
||||
|
||||
// data.
|
||||
var entities = await AsyncQueryableFactory.ToListAsync(((IQueryable<T>)CurrentQueryable), cancellationToken);
|
||||
var records = InterceptConvertTo<T>(entities);
|
||||
result.Data = records;
|
||||
|
||||
// aggregates.
|
||||
result.Aggregates = await CalculateTotalAggregateAsync<T>(afterFilterQueryable, cancellationToken);
|
||||
return result;
|
||||
}
|
||||
|
||||
protected virtual async Task<List<IAggregateResult>> CalculateTotalAggregateAsync<T>(IQueryable queryableAfterFilters, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!Criteria.Aggregates.Any())
|
||||
return null;
|
||||
|
||||
IQueryable selectExpression = CreateTotalAggregateSelectExpression<T>(queryableAfterFilters);
|
||||
var aggregateResult = await AsyncQueryableFactory.FirstOrDefaultAsync(selectExpression.Cast<DynamicClass>());
|
||||
return MaterializeCalculateTotalAggregateResult(aggregateResult);
|
||||
}
|
||||
|
||||
protected async virtual Task<List<List<DynamicClass>>> FetchAggregatesAsync<T>(List<IGroup> finalGroups, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!Criteria.Aggregates.Any())
|
||||
return null;
|
||||
|
||||
var previousGroups = new List<IGroup>();
|
||||
|
||||
var whenAllResult = await Task.WhenAll(finalGroups.Select(fg =>
|
||||
{
|
||||
IQueryable selectExpression = CreateFetchAggregateSelectExpression<T>(fg, previousGroups);
|
||||
var selectExpressionCasted = selectExpression.Cast<DynamicClass>();
|
||||
var aggregateResult = AsyncQueryableFactory.ToListAsync<DynamicClass>(selectExpressionCasted, cancellationToken);
|
||||
previousGroups.Add(fg);
|
||||
return aggregateResult;
|
||||
}));
|
||||
|
||||
var finalResult = whenAllResult.ToList();
|
||||
return finalResult;
|
||||
}
|
||||
}
|
||||
}
|
@ -31,6 +31,13 @@ namespace PoweredSoft.DynamicQuery
|
||||
CurrentQueryable = QueryableAtStart;
|
||||
}
|
||||
|
||||
protected virtual void CommonBeforeExecute<T>()
|
||||
{
|
||||
ApplyIncludeStrategyInterceptors<T>();
|
||||
ApplyBeforeFilterInterceptors<T>();
|
||||
ApplyFilters<T>();
|
||||
}
|
||||
|
||||
public virtual void AddInterceptor(IQueryInterceptor interceptor)
|
||||
{
|
||||
if (interceptor == null) throw new ArgumentNullException("interceptor");
|
||||
@ -83,6 +90,78 @@ namespace PoweredSoft.DynamicQuery
|
||||
});
|
||||
}
|
||||
|
||||
protected DynamicClass FindMatchingAggregateResult(List<List<DynamicClass>> aggregateResults, List<IGroup> groups, List<IGroupQueryResult> groupResults)
|
||||
{
|
||||
var groupIndex = groupResults.Count - 1;
|
||||
var aggregateLevel = aggregateResults[groupIndex];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
protected virtual IQueryable CreateFetchAggregateSelectExpression<T>(IGroup finalGroup, List<IGroup> previousGroups)
|
||||
{
|
||||
var groupExpression = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb =>
|
||||
{
|
||||
var groupKeyIndex = -1;
|
||||
previousGroups.ForEach(pg => gb.Path(pg.Path, $"Key_{++groupKeyIndex}"));
|
||||
gb.Path(finalGroup.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}");
|
||||
Criteria.Aggregates.ForEach((a, ai) =>
|
||||
{
|
||||
var fa = InterceptAggregate<T>(a);
|
||||
var selectType = ResolveSelectFrom(fa.Type);
|
||||
sb.Aggregate(fa.Path, selectType, $"Agg_{ai}");
|
||||
});
|
||||
});
|
||||
return selectExpression;
|
||||
}
|
||||
|
||||
protected virtual List<IAggregateResult> MaterializeCalculateTotalAggregateResult(DynamicClass aggregateResult)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
protected virtual IQueryable CreateTotalAggregateSelectExpression<T>(IQueryable queryableAfterFilters)
|
||||
{
|
||||
var groupExpression = queryableAfterFilters.EmptyGroupBy(QueryableUnderlyingType);
|
||||
var selectExpression = groupExpression.Select(sb =>
|
||||
{
|
||||
Criteria.Aggregates.ForEach((a, index) =>
|
||||
{
|
||||
var fa = InterceptAggregate<T>(a);
|
||||
var selectType = ResolveSelectFrom(fa.Type);
|
||||
sb.Aggregate(fa.Path, selectType, $"Agg_{index}");
|
||||
});
|
||||
});
|
||||
return selectExpression;
|
||||
}
|
||||
|
||||
protected virtual void CalculatePageCount(IQueryExecutionResult result)
|
||||
{
|
||||
if (!HasPaging)
|
||||
@ -249,5 +328,66 @@ namespace PoweredSoft.DynamicQuery
|
||||
.Cast<IBeforeQueryFilterInterceptor<T>>()
|
||||
.Aggregate((IQueryable<T>)CurrentQueryable, (prev, interceptor) => interceptor.InterceptBeforeFiltering(Criteria, prev));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ namespace PoweredSoft.DynamicQuery
|
||||
services.TryAddTransient<IGroup, Group>();
|
||||
services.TryAddTransient<IQueryCriteria, QueryCriteria>();
|
||||
services.TryAddTransient<IQueryHandler, QueryHandler>();
|
||||
services.TryAddTransient<IQueryHandlerAsync, QueryHandlerAsync>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user