diff --git a/PoweredSoft.DynamicQuery.AspNetCore/PoweredSoft.DynamicQuery.AspNetCore.csproj b/PoweredSoft.DynamicQuery.AspNetCore/PoweredSoft.DynamicQuery.AspNetCore.csproj index 23ccc15..efb4056 100644 --- a/PoweredSoft.DynamicQuery.AspNetCore/PoweredSoft.DynamicQuery.AspNetCore.csproj +++ b/PoweredSoft.DynamicQuery.AspNetCore/PoweredSoft.DynamicQuery.AspNetCore.csproj @@ -8,7 +8,7 @@ https://github.com/PoweredSoft/DynamicQuery github powered,soft,dynamic,criteria,query,builder,asp,net,core - 1.0.0$(VersionSuffix) + 2.0.0$(VersionSuffix) https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;r=g&amp;d=retro PoweredSoft.DynamicQuery.AspNetCore This projects makes it easier to use dynamic query in a asp.net core mvc project. diff --git a/PoweredSoft.DynamicQuery.Core/IAfterReadInterceptor.cs b/PoweredSoft.DynamicQuery.Core/IAfterReadInterceptor.cs index 3fd0aa8..d47b5ee 100644 --- a/PoweredSoft.DynamicQuery.Core/IAfterReadInterceptor.cs +++ b/PoweredSoft.DynamicQuery.Core/IAfterReadInterceptor.cs @@ -24,4 +24,14 @@ namespace PoweredSoft.DynamicQuery.Core { Task AfterReadAsync(List> pairs, CancellationToken cancellationToken = default(CancellationToken)); } + + public interface IAfterReadInterceptor : IQueryInterceptor + { + void AfterRead(List> pairs); + } + + public interface IAfterReadInterceptorAsync : IQueryInterceptor + { + Task AfterReadAsync(List> pairs, CancellationToken cancellationToken = default(CancellationToken)); + } } diff --git a/PoweredSoft.DynamicQuery.Core/IQueryConvertInterceptor.cs b/PoweredSoft.DynamicQuery.Core/IQueryConvertInterceptor.cs index df386a8..f204a13 100644 --- a/PoweredSoft.DynamicQuery.Core/IQueryConvertInterceptor.cs +++ b/PoweredSoft.DynamicQuery.Core/IQueryConvertInterceptor.cs @@ -13,4 +13,9 @@ namespace PoweredSoft.DynamicQuery.Core { object InterceptResultTo(T entity); } + + public interface IQueryConvertInterceptor : IQueryInterceptor + { + T2 InterceptResultTo(T entity); + } } diff --git a/PoweredSoft.DynamicQuery.Core/IQueryHandler.cs b/PoweredSoft.DynamicQuery.Core/IQueryHandler.cs index cdf5a43..6a818ff 100644 --- a/PoweredSoft.DynamicQuery.Core/IQueryHandler.cs +++ b/PoweredSoft.DynamicQuery.Core/IQueryHandler.cs @@ -13,11 +13,13 @@ namespace PoweredSoft.DynamicQuery.Core public interface IQueryHandler : IInterceptableQueryHandler { - IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria); + IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria); + IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria); } public interface IQueryHandlerAsync : IInterceptableQueryHandler { - Task ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)); + Task> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)); + Task> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)); } } diff --git a/PoweredSoft.DynamicQuery.Core/IQueryResult.cs b/PoweredSoft.DynamicQuery.Core/IQueryResult.cs index d42fceb..cb7b6d4 100644 --- a/PoweredSoft.DynamicQuery.Core/IQueryResult.cs +++ b/PoweredSoft.DynamicQuery.Core/IQueryResult.cs @@ -11,23 +11,35 @@ namespace PoweredSoft.DynamicQuery.Core object Value { get; set; } } - public interface IQueryResult + public interface IQueryResult { List Aggregates { get; } - List Data { get; } + List Data { get; } } - public interface IGroupQueryResult : IQueryResult + public interface IGroupQueryResult : IQueryResult { string GroupPath { get; set; } object GroupValue { get; set; } + bool HasSubGroups { get; } + List> SubGroups { get; set; } } - public interface IQueryExecutionResult : IQueryResult + public interface IQueryExecutionResultPaging { long TotalRecords { get; set; } long? NumberOfPages { get; set; } } - + public interface IQueryExecutionResult : IQueryResult, IQueryExecutionResultPaging + { + + } + + public interface IQueryExecutionGroupResult : IQueryExecutionResult + { + List> Groups { get; set; } + } + + } diff --git a/PoweredSoft.DynamicQuery.Core/PoweredSoft.DynamicQuery.Core.csproj b/PoweredSoft.DynamicQuery.Core/PoweredSoft.DynamicQuery.Core.csproj index 02e5841..cc4ce77 100644 --- a/PoweredSoft.DynamicQuery.Core/PoweredSoft.DynamicQuery.Core.csproj +++ b/PoweredSoft.DynamicQuery.Core/PoweredSoft.DynamicQuery.Core.csproj @@ -8,7 +8,7 @@ https://github.com/PoweredSoft/DynamicQuery.Core/ github powered,soft,dynamic,criteria,query,builder - 1.0.0$(VersionSuffix) + 2.0.0$(VersionSuffix) https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;r=g&amp;d=retro PoweredSoft.DynamicQuery.Core core abstractions diff --git a/PoweredSoft.DynamicQuery.Test/AggregateTests.cs b/PoweredSoft.DynamicQuery.Test/AggregateTests.cs index cee7f3c..cb36f65 100644 --- a/PoweredSoft.DynamicQuery.Test/AggregateTests.cs +++ b/PoweredSoft.DynamicQuery.Test/AggregateTests.cs @@ -114,7 +114,11 @@ namespace PoweredSoft.DynamicQuery.Test var queryHandler = new QueryHandler(); var result = queryHandler.Execute(ctx.OrderItems, criteria); - var groups = result.Data.Cast().ToList(); + + var groupedResult = result as IQueryExecutionGroupResult; + Assert.NotNull(groupedResult); + + var groups = groupedResult.Groups; // validate group and aggregates of groups. Assert.Equal(groups.Count, shouldResults.Count); diff --git a/PoweredSoft.DynamicQuery.Test/AsyncTests.cs b/PoweredSoft.DynamicQuery.Test/AsyncTests.cs index 26ad8b9..fc74c29 100644 --- a/PoweredSoft.DynamicQuery.Test/AsyncTests.cs +++ b/PoweredSoft.DynamicQuery.Test/AsyncTests.cs @@ -1,6 +1,7 @@ using PoweredSoft.Data; using PoweredSoft.Data.EntityFrameworkCore; using PoweredSoft.DynamicQuery.Core; +using PoweredSoft.DynamicQuery.Extensions; using PoweredSoft.DynamicQuery.Test.Mock; using System; using System.Collections.Generic; @@ -65,7 +66,7 @@ namespace PoweredSoft.DynamicQuery.Test var asyncService = new AsyncQueryableService(new[] { new AsyncQueryableHandlerService() }); var queryHandler = new QueryHandlerAsync(asyncService); var result = await queryHandler.ExecuteAsync(ctx.OrderItems, criteria); - var groups = result.Data.Cast().ToList(); + var groups = result.GroupedResult().Groups; // validate group and aggregates of groups. Assert.Equal(groups.Count, shouldResults.Count); diff --git a/PoweredSoft.DynamicQuery.Test/ConvertibleInterceptorTests.cs b/PoweredSoft.DynamicQuery.Test/ConvertibleInterceptorTests.cs index d43990d..0533d7c 100644 --- a/PoweredSoft.DynamicQuery.Test/ConvertibleInterceptorTests.cs +++ b/PoweredSoft.DynamicQuery.Test/ConvertibleInterceptorTests.cs @@ -55,6 +55,22 @@ namespace PoweredSoft.DynamicQuery.Test } } + private class MockQueryConvertGenericInterceptor2 : + IQueryConvertInterceptor + { + public CustomerModel InterceptResultTo(Customer entity) + { + var customer = entity; + var personModel = new CustomerModel + { + Id = customer.Id, + FirstName = customer.FirstName, + LastName = customer.LastName + }; + return personModel; + } + } + [Fact] public void NonGeneric() { @@ -63,7 +79,7 @@ namespace PoweredSoft.DynamicQuery.Test var criteria = new QueryCriteria(); var queryHandler = new QueryHandler(); queryHandler.AddInterceptor(new MockQueryConvertInterceptor()); - var result = queryHandler.Execute(ctx.Customers, criteria); + var result = queryHandler.Execute(ctx.Customers, criteria); Assert.All(result.Data, t => Assert.IsType(t)); }); } @@ -76,7 +92,20 @@ namespace PoweredSoft.DynamicQuery.Test var criteria = new QueryCriteria(); var queryHandler = new QueryHandler(); queryHandler.AddInterceptor(new MockQueryConvertGenericInterceptor()); - var result = queryHandler.Execute(ctx.Customers, criteria); + var result = queryHandler.Execute(ctx.Customers, criteria); + Assert.All(result.Data, t => Assert.IsType(t)); + }); + } + + [Fact] + public void Generic2() + { + MockContextFactory.SeedAndTestContextFor("ConvertibleIntereceptorTests_Generic2", TestSeeders.SimpleSeedScenario, ctx => + { + var criteria = new QueryCriteria(); + var queryHandler = new QueryHandler(); + queryHandler.AddInterceptor(new MockQueryConvertGenericInterceptor2()); + var result = queryHandler.Execute(ctx.Customers, criteria); Assert.All(result.Data, t => Assert.IsType(t)); }); } diff --git a/PoweredSoft.DynamicQuery.Test/GroupInterceptorTests.cs b/PoweredSoft.DynamicQuery.Test/GroupInterceptorTests.cs index 8a6a111..42e3d06 100644 --- a/PoweredSoft.DynamicQuery.Test/GroupInterceptorTests.cs +++ b/PoweredSoft.DynamicQuery.Test/GroupInterceptorTests.cs @@ -1,4 +1,5 @@ using PoweredSoft.DynamicQuery.Core; +using PoweredSoft.DynamicQuery.Extensions; using PoweredSoft.DynamicQuery.Test.Mock; using System; using System.Collections.Generic; @@ -37,7 +38,9 @@ namespace PoweredSoft.DynamicQuery.Test var queryHandler = new QueryHandler(); queryHandler.AddInterceptor(new MockGroupInterceptor()); var result = queryHandler.Execute(ctx.Orders, criteria); - var actual = result.Data.Cast().Select(t => t.GroupValue).ToList(); + + var groupedResult = result.GroupedResult(); + var actual = groupedResult.Groups.Select(t => t.GroupValue).ToList(); Assert.Equal(expected, actual); }); } diff --git a/PoweredSoft.DynamicQuery.Test/GroupTests.cs b/PoweredSoft.DynamicQuery.Test/GroupTests.cs index 05efd1b..5f9c3b2 100644 --- a/PoweredSoft.DynamicQuery.Test/GroupTests.cs +++ b/PoweredSoft.DynamicQuery.Test/GroupTests.cs @@ -1,4 +1,5 @@ using PoweredSoft.DynamicQuery.Core; +using PoweredSoft.DynamicQuery.Extensions; using PoweredSoft.DynamicQuery.Test.Mock; using System; using System.Collections.Generic; @@ -34,13 +35,14 @@ namespace PoweredSoft.DynamicQuery.Test var queryHandler = new QueryHandler(); var result = queryHandler.Execute(ctx.Orders, criteria); + var groupedResult = result.GroupedResult(); // top level should have same amount of group levels. - Assert.Equal(result.Data.Count, shouldResult.Count); + Assert.Equal(groupedResult.Groups.Count, shouldResult.Count); for (var i = 0; i < shouldResult.Count; i++) { var expected = shouldResult[0]; - var actual = ((IGroupQueryResult)result.Data[0]); + var actual = groupedResult.Groups[0]; Assert.Equal(expected.Customer.Id, (actual.GroupValue as Customer).Id); var expectedOrderIds = expected.Orders.Select(t => t.Id).ToList(); @@ -71,13 +73,15 @@ namespace PoweredSoft.DynamicQuery.Test var queryHandler = new QueryHandler(); var result = queryHandler.Execute(ctx.Tickets, criteria); - var firstGroup = result.Data[0] as IGroupQueryResult; + var groupedResult = result.GroupedResult(); + + var firstGroup = groupedResult.Groups.FirstOrDefault(); Assert.NotNull(firstGroup); - var secondGroup = result.Data[1] as IGroupQueryResult; + var secondGroup = groupedResult.Groups.Skip(1).FirstOrDefault(); Assert.NotNull(secondGroup); var expected = ctx.Tickets.Select(t => t.TicketType).Distinct().Count(); - var c = result.Data.Cast().Select(t => t.GroupValue).Count(); + var c = groupedResult.Groups.Select(t => t.GroupValue).Count(); Assert.Equal(expected, c); }); } @@ -102,7 +106,7 @@ namespace PoweredSoft.DynamicQuery.Test var interceptor = new InterceptorsWithGrouping(); var queryHandler = new QueryHandler(); queryHandler.AddInterceptor(interceptor); - var result = queryHandler.Execute(ctx.Tickets, criteria); + var result = queryHandler.Execute(ctx.Tickets, criteria); Assert.Equal(4, interceptor.Count); Assert.True(interceptor.Test); Assert.True(interceptor.Test2); diff --git a/PoweredSoft.DynamicQuery/Extensions/GroupResultExtensions.cs b/PoweredSoft.DynamicQuery/Extensions/GroupResultExtensions.cs new file mode 100644 index 0000000..6dcdb39 --- /dev/null +++ b/PoweredSoft.DynamicQuery/Extensions/GroupResultExtensions.cs @@ -0,0 +1,18 @@ +using PoweredSoft.DynamicQuery.Core; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PoweredSoft.DynamicQuery.Extensions +{ + public static class GroupResultExtensions + { + public static IQueryExecutionGroupResult GroupedResult(this IQueryExecutionResult source) + { + if (source is IQueryExecutionGroupResult ret) + return ret; + + throw new Exception("this result is not a grouped result"); + } + } +} diff --git a/PoweredSoft.DynamicQuery/PoweredSoft.DynamicQuery.csproj b/PoweredSoft.DynamicQuery/PoweredSoft.DynamicQuery.csproj index aefa7bb..c7bb118 100644 --- a/PoweredSoft.DynamicQuery/PoweredSoft.DynamicQuery.csproj +++ b/PoweredSoft.DynamicQuery/PoweredSoft.DynamicQuery.csproj @@ -8,7 +8,7 @@ https://github.com/PoweredSoft/DynamicQuery github powered,soft,dynamic,criteria,query,builder - 1.0.0$(VersionSuffix) + 2.0.0$(VersionSuffix) https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;r=g&amp;d=retro PoweredSoft.DynamicQuery dynamic query based on string path very usefull for network requests. diff --git a/PoweredSoft.DynamicQuery/QueryHandler.cs b/PoweredSoft.DynamicQuery/QueryHandler.cs index 1e2a5c0..bff122a 100644 --- a/PoweredSoft.DynamicQuery/QueryHandler.cs +++ b/PoweredSoft.DynamicQuery/QueryHandler.cs @@ -4,6 +4,7 @@ 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; @@ -13,19 +14,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[] { }); - - protected virtual IQueryExecutionResult Execute() + protected virtual IQueryExecutionResult FinalExecute() { - CommonBeforeExecute(); - return HasGrouping ? ExecuteGrouping() : ExecuteNoGrouping(); + CommonBeforeExecute(); + return HasGrouping ? ExecuteGrouping() : ExecuteNoGrouping(); } - - protected virtual IQueryExecutionResult ExecuteGrouping() + protected virtual IQueryExecutionResult ExecuteGrouping() { - var result = new QueryExecutionResult(); + var result = new QueryExecutionGroupResult(); // preserve queryable. var queryableAfterFilters = CurrentQueryable; @@ -34,17 +31,17 @@ namespace PoweredSoft.DynamicQuery CalculatePageCount(result); // intercept groups in advance to avoid doing it more than once :) - var finalGroups = Criteria.Groups.Select(g => InterceptGroup(g)).ToList(); + var finalGroups = Criteria.Groups.Select(g => InterceptGroup(g)).ToList(); // get the aggregates. - var aggregateResults = FetchAggregates(finalGroups); + var aggregateResults = FetchAggregates(finalGroups); // sorting. finalGroups.ForEach(fg => Criteria.Sorts.Insert(0, new Sort(fg.Path, fg.Ascending))); // apply sorting and paging. - ApplySorting(); - ApplyPaging(); + ApplySorting(); + ApplyPaging(); // create group & select expression. CurrentQueryable = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb => finalGroups.ForEach((fg, index) => gb.Path(fg.Path, $"Key_{index}"))); @@ -58,26 +55,26 @@ namespace PoweredSoft.DynamicQuery var groupRecords = CurrentQueryable.ToDynamicClassList(); // now join them into logical collections - var lastLists = new List>(); - result.Data = RecursiveRegroup(groupRecords, aggregateResults, Criteria.Groups.First(), lastLists); + var lastLists = new List<(List source, IGroupQueryResult group)>(); + result.Groups = RecursiveRegroup(groupRecords, aggregateResults, Criteria.Groups.First(), lastLists); // intercept grouped by. - QueryInterceptToGrouped(lastLists).Wait(); + QueryInterceptToGrouped(lastLists).Wait(); - result.Aggregates = CalculateTotalAggregate(queryableAfterFilters); + result.Aggregates = CalculateTotalAggregate(queryableAfterFilters); return result; } - protected virtual List CalculateTotalAggregate(IQueryable queryableAfterFilters) + protected virtual List CalculateTotalAggregate(IQueryable queryableAfterFilters) { if (!Criteria.Aggregates.Any()) return null; - IQueryable selectExpression = CreateTotalAggregateSelectExpression(queryableAfterFilters); + IQueryable selectExpression = CreateTotalAggregateSelectExpression(queryableAfterFilters); var aggregateResult = selectExpression.ToDynamicClassList().FirstOrDefault(); return MaterializeCalculateTotalAggregateResult(aggregateResult); } - protected virtual List> FetchAggregates(List finalGroups) + protected virtual List> FetchAggregates(List finalGroups) { if (!Criteria.Aggregates.Any()) return null; @@ -85,7 +82,7 @@ namespace PoweredSoft.DynamicQuery var previousGroups = new List(); var ret = finalGroups.Select(fg => { - IQueryable selectExpression = CreateFetchAggregateSelectExpression(fg, previousGroups); + IQueryable selectExpression = CreateFetchAggregateSelectExpression(fg, previousGroups); var aggregateResult = selectExpression.ToDynamicClassList(); previousGroups.Add(fg); return aggregateResult; @@ -93,9 +90,9 @@ namespace PoweredSoft.DynamicQuery return ret; } - protected virtual IQueryExecutionResult ExecuteNoGrouping() + protected virtual IQueryExecutionResult ExecuteNoGrouping() { - var result = new QueryExecutionResult(); + var result = new QueryExecutionResult(); // after filter queryable var afterFilterQueryable = CurrentQueryable; @@ -105,25 +102,30 @@ namespace PoweredSoft.DynamicQuery CalculatePageCount(result); // sorts and paging. - ApplySorting(); - ApplyPaging(); + ApplySorting(); + ApplyPaging(); // data. - var entities = ((IQueryable)CurrentQueryable).ToList(); - var records = InterceptConvertTo(entities).Result; + var entities = ((IQueryable)CurrentQueryable).ToList(); + var records = InterceptConvertTo(entities).Result; result.Data = records; // aggregates. - result.Aggregates = CalculateTotalAggregate(afterFilterQueryable); + result.Aggregates = CalculateTotalAggregate(afterFilterQueryable); return result; } - - public virtual IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria) + public IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria) { Reset(queryable, criteria); - return ExecuteReflected(); + return FinalExecute(); + } + + public IQueryExecutionResult Execute(IQueryable queryable, IQueryCriteria criteria) + { + Reset(queryable, criteria); + return FinalExecute(); } } } diff --git a/PoweredSoft.DynamicQuery/QueryHandlerAsync.cs b/PoweredSoft.DynamicQuery/QueryHandlerAsync.cs index 23ddbb7..9ffe598 100644 --- a/PoweredSoft.DynamicQuery/QueryHandlerAsync.cs +++ b/PoweredSoft.DynamicQuery/QueryHandlerAsync.cs @@ -3,60 +3,52 @@ using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using PoweredSoft.Data; using PoweredSoft.Data.Core; using PoweredSoft.DynamicLinq; using PoweredSoft.DynamicQuery.Core; namespace PoweredSoft.DynamicQuery { + public class QueryHandlerAsync : QueryHandlerBase, IQueryHandlerAsync { - internal MethodInfo ExecuteAsyncGeneric = typeof(QueryHandlerAsync).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).First(t => t.Name == "ExecuteAsync" && t.IsGenericMethod); - public IAsyncQueryableService AsyncQueryableService { get; } - internal Task ExecuteAsyncReflected(CancellationToken cancellationToken) => (Task)ExecuteAsyncGeneric.MakeGenericMethod(QueryableUnderlyingType).Invoke(this, new object[] { cancellationToken }); - public QueryHandlerAsync(IAsyncQueryableService asyncQueryableService) { AsyncQueryableService = asyncQueryableService; } - protected virtual Task ExecuteAsync(CancellationToken cancellationToken = default(CancellationToken)) + protected virtual Task> FinalExecuteAsync(CancellationToken cancellationToken = default(CancellationToken)) { - CommonBeforeExecute(); - return HasGrouping ? ExecuteAsyncGrouping(cancellationToken) : ExecuteAsyncNoGrouping(cancellationToken); + CommonBeforeExecute(); + return HasGrouping ? ExecuteAsyncGrouping(cancellationToken) : ExecuteAsyncNoGrouping(cancellationToken); } - public Task ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)) + protected virtual async Task> ExecuteAsyncGrouping(CancellationToken cancellationToken) { - Reset(queryable, criteria); - return ExecuteAsyncReflected(cancellationToken); - } - - protected virtual async Task ExecuteAsyncGrouping(CancellationToken cancellationToken) - { - var result = new QueryExecutionResult(); + var result = new QueryExecutionGroupResult(); // preserve queryable. var queryableAfterFilters = CurrentQueryable; // async. - result.TotalRecords = await this.AsyncQueryableService.LongCountAsync((IQueryable)queryableAfterFilters, cancellationToken); + result.TotalRecords = await this.AsyncQueryableService.LongCountAsync((IQueryable)queryableAfterFilters, cancellationToken); CalculatePageCount(result); // intercept groups in advance to avoid doing it more than once :) - var finalGroups = Criteria.Groups.Select(g => InterceptGroup(g)).ToList(); + var finalGroups = Criteria.Groups.Select(g => InterceptGroup(g)).ToList(); // get the aggregates. - var aggregateResults = await FetchAggregatesAsync(finalGroups, cancellationToken); + var aggregateResults = await FetchAggregatesAsync(finalGroups, cancellationToken); // sorting. finalGroups.ForEach(fg => Criteria.Sorts.Insert(0, new Sort(fg.Path, fg.Ascending))); // apply sorting and paging. - ApplySorting(); - ApplyPaging(); + ApplySorting(); + ApplyPaging(); // create group & select expression. CurrentQueryable = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb => finalGroups.ForEach((fg, index) => gb.Path(fg.Path, $"Key_{index}"))); @@ -70,52 +62,52 @@ namespace PoweredSoft.DynamicQuery var groupRecords = await AsyncQueryableService.ToListAsync(CurrentQueryable.Cast(), cancellationToken); // now join them into logical collections - var lastLists = new List>(); - result.Data = RecursiveRegroup(groupRecords, aggregateResults, Criteria.Groups.First(), lastLists); + var lastLists = new List<(List entities, IGroupQueryResult group)>(); + result.Groups = RecursiveRegroup(groupRecords, aggregateResults, Criteria.Groups.First(), lastLists); // converted to grouped by. - await QueryInterceptToGrouped(lastLists); + await QueryInterceptToGrouped(lastLists); - result.Aggregates = await CalculateTotalAggregateAsync(queryableAfterFilters, cancellationToken); + result.Aggregates = await CalculateTotalAggregateAsync(queryableAfterFilters, cancellationToken); return result; } - protected async Task ExecuteAsyncNoGrouping(CancellationToken cancellationToken) + protected async Task> ExecuteAsyncNoGrouping(CancellationToken cancellationToken) { - var result = new QueryExecutionResult(); + var result = new QueryExecutionResult(); // after filter queryable - IQueryable afterFilterQueryable = (IQueryable)CurrentQueryable; + IQueryable afterFilterQueryable = (IQueryable)CurrentQueryable; // total records. result.TotalRecords = await AsyncQueryableService.LongCountAsync(afterFilterQueryable, cancellationToken); CalculatePageCount(result); // sorts and paging. - ApplySorting(); - ApplyPaging(); + ApplySorting(); + ApplyPaging(); // data. - var entities = await AsyncQueryableService.ToListAsync(((IQueryable)CurrentQueryable), cancellationToken); - var records = await InterceptConvertTo(entities); + var entities = await AsyncQueryableService.ToListAsync(((IQueryable)CurrentQueryable), cancellationToken); + var records = await InterceptConvertTo(entities); result.Data = records; // aggregates. - result.Aggregates = await CalculateTotalAggregateAsync(afterFilterQueryable, cancellationToken); + result.Aggregates = await CalculateTotalAggregateAsync(afterFilterQueryable, cancellationToken); return result; } - protected virtual async Task> CalculateTotalAggregateAsync(IQueryable queryableAfterFilters, CancellationToken cancellationToken) + protected virtual async Task> CalculateTotalAggregateAsync(IQueryable queryableAfterFilters, CancellationToken cancellationToken) { if (!Criteria.Aggregates.Any()) return null; - IQueryable selectExpression = CreateTotalAggregateSelectExpression(queryableAfterFilters); + IQueryable selectExpression = CreateTotalAggregateSelectExpression(queryableAfterFilters); var aggregateResult = await AsyncQueryableService.FirstOrDefaultAsync(selectExpression.Cast()); return MaterializeCalculateTotalAggregateResult(aggregateResult); } - protected async virtual Task>> FetchAggregatesAsync(List finalGroups, CancellationToken cancellationToken) + protected async virtual Task>> FetchAggregatesAsync(List finalGroups, CancellationToken cancellationToken) { if (!Criteria.Aggregates.Any()) return null; @@ -124,7 +116,7 @@ namespace PoweredSoft.DynamicQuery var whenAllResult = await Task.WhenAll(finalGroups.Select(fg => { - IQueryable selectExpression = CreateFetchAggregateSelectExpression(fg, previousGroups); + IQueryable selectExpression = CreateFetchAggregateSelectExpression(fg, previousGroups); var selectExpressionCasted = selectExpression.Cast(); var aggregateResult = AsyncQueryableService.ToListAsync(selectExpressionCasted, cancellationToken); previousGroups.Add(fg); @@ -134,5 +126,17 @@ namespace PoweredSoft.DynamicQuery var finalResult = whenAllResult.ToList(); return finalResult; } + + public Task> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)) + { + Reset(queryable, criteria); + return FinalExecuteAsync(cancellationToken); + } + + public Task> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)) + { + Reset(queryable, criteria); + return FinalExecuteAsync(cancellationToken); + } } } diff --git a/PoweredSoft.DynamicQuery/QueryHandlerBase.cs b/PoweredSoft.DynamicQuery/QueryHandlerBase.cs index f0f21c4..29da988 100644 --- a/PoweredSoft.DynamicQuery/QueryHandlerBase.cs +++ b/PoweredSoft.DynamicQuery/QueryHandlerBase.cs @@ -31,11 +31,11 @@ namespace PoweredSoft.DynamicQuery CurrentQueryable = QueryableAtStart; } - protected virtual void CommonBeforeExecute() + protected virtual void CommonBeforeExecute() { - ApplyIncludeStrategyInterceptors(); - ApplyBeforeFilterInterceptors(); - ApplyFilters(); + ApplyIncludeStrategyInterceptors(); + ApplyBeforeFilterInterceptors(); + ApplyFilters(); } public virtual void AddInterceptor(IQueryInterceptor interceptor) @@ -46,7 +46,7 @@ namespace PoweredSoft.DynamicQuery Interceptors.Add(interceptor); } - protected virtual IGroup InterceptGroup(IGroup group) + protected virtual IGroup InterceptGroup(IGroup group) { var ret = Interceptors .Where(t => t is IGroupInterceptor) @@ -57,28 +57,28 @@ namespace PoweredSoft.DynamicQuery } - protected virtual void ApplyPaging() + protected virtual void ApplyPaging() { if (!HasPaging) return; - var q = (IQueryable) CurrentQueryable; + var q = (IQueryable) CurrentQueryable; var skip = ((Criteria.Page ?? 1) - 1) * Criteria.PageSize.Value; CurrentQueryable = q.Skip(skip).Take(Criteria.PageSize.Value); } - protected virtual void ApplySorting() + protected virtual void ApplySorting() { if (Criteria.Sorts?.Any() != true) { - ApplyNoSortInterceptor(); + ApplyNoSortInterceptor(); return; } bool isAppending = false; Criteria.Sorts.ForEach(sort => { - var transformedSort = InterceptSort(sort); + var transformedSort = InterceptSort(sort); if (transformedSort.Count == 0) return; @@ -90,7 +90,7 @@ namespace PoweredSoft.DynamicQuery }); } - protected DynamicClass FindMatchingAggregateResult(List> aggregateResults, List groups, List groupResults) + protected DynamicClass FindMatchingAggregateResult(List> aggregateResults, List groups, List> groupResults) { var groupIndex = groupResults.Count - 1; var aggregateLevel = aggregateResults[groupIndex]; @@ -108,7 +108,7 @@ namespace PoweredSoft.DynamicQuery return ret; } - protected virtual IQueryable CreateFetchAggregateSelectExpression(IGroup finalGroup, List previousGroups) + protected virtual IQueryable CreateFetchAggregateSelectExpression(IGroup finalGroup, List previousGroups) { var groupExpression = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb => { @@ -124,7 +124,7 @@ namespace PoweredSoft.DynamicQuery sb.Key($"Key_{++groupKeyIndex}", $"Key_{groupKeyIndex}"); Criteria.Aggregates.ForEach((a, ai) => { - var fa = InterceptAggregate(a); + var fa = InterceptAggregate(a); var selectType = ResolveSelectFrom(fa.Type); sb.Aggregate(fa.Path, selectType, $"Agg_{ai}"); }); @@ -147,14 +147,14 @@ namespace PoweredSoft.DynamicQuery return ret; } - protected virtual IQueryable CreateTotalAggregateSelectExpression(IQueryable queryableAfterFilters) + protected virtual IQueryable CreateTotalAggregateSelectExpression(IQueryable queryableAfterFilters) { var groupExpression = queryableAfterFilters.EmptyGroupBy(QueryableUnderlyingType); var selectExpression = groupExpression.Select(sb => { Criteria.Aggregates.ForEach((a, index) => { - var fa = InterceptAggregate(a); + var fa = InterceptAggregate(a); var selectType = ResolveSelectFrom(fa.Type); sb.Aggregate(fa.Path, selectType, $"Agg_{index}"); }); @@ -162,7 +162,7 @@ namespace PoweredSoft.DynamicQuery return selectExpression; } - protected virtual void CalculatePageCount(IQueryExecutionResult result) + protected virtual void CalculatePageCount(IQueryExecutionResultPaging result) { if (!HasPaging) return; @@ -173,7 +173,7 @@ namespace PoweredSoft.DynamicQuery result.NumberOfPages = result.TotalRecords / Criteria.PageSize + (result.TotalRecords % Criteria.PageSize != 0 ? 1 : 0); } - protected virtual IAggregate InterceptAggregate(IAggregate aggregate) + protected virtual IAggregate InterceptAggregate(IAggregate aggregate) { var ret = Interceptors .Where(t => t is IAggregateInterceptor) @@ -182,47 +182,53 @@ namespace PoweredSoft.DynamicQuery return ret; } - protected virtual async Task> InterceptConvertTo(List entities) + protected virtual async Task> InterceptConvertTo(List entities) { await AfterEntityReadInterceptors(entities); - var objects = entities.Cast().ToList(); - for (var i = 0; i < objects.Count; i++) - objects[i] = InterceptConvertToObject(objects[i]); + var ret = new List(); + for (var i = 0; i < entities.Count; i++) + ret.Add(InterceptConvertToObject(entities[i])); - var pairs = entities.Select((t, index) => Tuple.Create(t, objects[index])).ToList(); - await AfterReadInterceptors(pairs); + var pairs = entities.Select((t, index) => Tuple.Create(t, ret[index])).ToList(); + await AfterReadInterceptors(pairs); - return objects; + return ret; } - protected virtual async Task AfterEntityReadInterceptors(List entities) + protected virtual async Task AfterEntityReadInterceptors(List entities) { Interceptors - .Where(t => t is IAfterReadEntityInterceptor) - .Cast>() + .Where(t => t is IAfterReadEntityInterceptor) + .Cast>() .ToList() .ForEach(t => t.AfterReadEntity(entities)); - var asyncInterceptors = Interceptors.Where(t => t is IAfterReadEntityInterceptorAsync).Cast>(); + var asyncInterceptors = Interceptors.Where(t => t is IAfterReadEntityInterceptorAsync).Cast>(); foreach (var interceptor in asyncInterceptors) await interceptor.AfterReadEntityAsync(entities); } - protected virtual async Task AfterReadInterceptors(List> pairs) + protected virtual async Task AfterReadInterceptors(List> pairs) { - Interceptors - .Where(t => t is IAfterReadInterceptor) - .Cast>() - .ToList() - .ForEach(t => t.AfterRead(pairs)); + var objPair = pairs.Select(t => Tuple.Create(t.Item1, (object)t.Item2)).ToList(); - var asyncInterceptors = Interceptors.Where(t => t is IAfterReadInterceptorAsync).Cast>(); + Interceptors + .Where(t => t is IAfterReadInterceptor) + .Cast>() + .ToList() + .ForEach(t => t.AfterRead(objPair)); + + var asyncInterceptors = Interceptors.Where(t => t is IAfterReadInterceptorAsync).Cast>(); foreach (var interceptor in asyncInterceptors) + await interceptor.AfterReadAsync(objPair); + + var asyncInterceptors2 = Interceptors.Where(t => t is IAfterReadInterceptorAsync).Cast>(); + foreach (var interceptor in asyncInterceptors2) await interceptor.AfterReadAsync(pairs); } - protected virtual object InterceptConvertToObject(object o) + protected virtual TRecord InterceptConvertToObject(object o) { o = Interceptors .Where(t => t is IQueryConvertInterceptor) @@ -230,20 +236,31 @@ namespace PoweredSoft.DynamicQuery .Aggregate(o, (prev, interceptor) => interceptor.InterceptResultTo(prev)); o = Interceptors - .Where(t => t is IQueryConvertInterceptor) - .Cast>() + .Where(t => t is IQueryConvertInterceptor) + .Cast>() .Aggregate(o, (prev, interceptor) => { - if (prev is T) - return interceptor.InterceptResultTo((T)prev); + if (prev is TSource) + return interceptor.InterceptResultTo((TSource)prev); return o; }); - return o; + o = Interceptors + .Where(t => t is IQueryConvertInterceptor) + .Cast>() + .Aggregate(o, (prev, interceptor) => + { + if (prev is TSource) + return interceptor.InterceptResultTo((TSource)prev); + + return o; + }); + + return (TRecord)o; } - protected virtual List InterceptSort(ISort sort) + protected virtual List InterceptSort(ISort sort) { var original = new List() { @@ -259,15 +276,15 @@ namespace PoweredSoft.DynamicQuery return ret.ToList(); } - protected virtual void ApplyNoSortInterceptor() + protected virtual void ApplyNoSortInterceptor() { CurrentQueryable = Interceptors.Where(t => t is INoSortInterceptor) .Cast() .Aggregate(CurrentQueryable, (prev, interceptor) => interceptor.InterceptNoSort(Criteria, prev)); - CurrentQueryable = Interceptors.Where(t => t is INoSortInterceptor) - .Cast>() - .Aggregate((IQueryable)CurrentQueryable, (prev, interceptor) => interceptor.InterceptNoSort(Criteria, prev)); + CurrentQueryable = Interceptors.Where(t => t is INoSortInterceptor) + .Cast>() + .Aggregate((IQueryable)CurrentQueryable, (prev, interceptor) => interceptor.InterceptNoSort(Criteria, prev)); } @@ -292,40 +309,40 @@ namespace PoweredSoft.DynamicQuery return ret.Value; } - protected virtual void ApplyFilters() + protected virtual void ApplyFilters() { if (true != Criteria.Filters?.Any()) return; CurrentQueryable = CurrentQueryable.Query(whereBuilder => { - Criteria.Filters.ForEach(filter => ApplyFilter(whereBuilder, filter)); + Criteria.Filters.ForEach(filter => ApplyFilter(whereBuilder, filter)); }); } - protected virtual void ApplyFilter(WhereBuilder whereBuilder, IFilter filter) + protected virtual void ApplyFilter(WhereBuilder whereBuilder, IFilter filter) { - var transformedFilter = InterceptFilter(filter); + var transformedFilter = InterceptFilter(filter); if (transformedFilter is ISimpleFilter) - ApplySimpleFilter(whereBuilder, transformedFilter as ISimpleFilter); + ApplySimpleFilter(whereBuilder, transformedFilter as ISimpleFilter); else if (transformedFilter is ICompositeFilter) - AppleCompositeFilter(whereBuilder, transformedFilter as ICompositeFilter); + AppleCompositeFilter(whereBuilder, transformedFilter as ICompositeFilter); else throw new NotSupportedException(); } - protected virtual void AppleCompositeFilter(WhereBuilder whereBuilder, ICompositeFilter filter) + protected virtual void AppleCompositeFilter(WhereBuilder whereBuilder, ICompositeFilter filter) { - whereBuilder.SubQuery(subWhereBuilder => filter.Filters.ForEach(subFilter => ApplyFilter(subWhereBuilder, subFilter)), filter.And == true); + whereBuilder.SubQuery(subWhereBuilder => filter.Filters.ForEach(subFilter => ApplyFilter(subWhereBuilder, subFilter)), filter.And == true); } - protected virtual void ApplySimpleFilter(WhereBuilder whereBuilder, ISimpleFilter filter) + protected virtual void ApplySimpleFilter(WhereBuilder whereBuilder, ISimpleFilter filter) { var resolvedConditionOperator = ResolveConditionOperatorFrom(filter.Type); whereBuilder.Compare(filter.Path, resolvedConditionOperator, filter.Value, and: filter.And == true); } - protected virtual IFilter InterceptFilter(IFilter filter) + protected virtual IFilter InterceptFilter(IFilter filter) { var ret = Interceptors.Where(t => t is IFilterInterceptor) .Cast() @@ -334,7 +351,7 @@ namespace PoweredSoft.DynamicQuery return ret; } - protected virtual void ApplyIncludeStrategyInterceptors() + protected virtual void ApplyIncludeStrategyInterceptors() { CurrentQueryable = Interceptors .Where(t => t is IIncludeStrategyInterceptor) @@ -342,12 +359,12 @@ namespace PoweredSoft.DynamicQuery .Aggregate(CurrentQueryable, (prev, interceptor) => interceptor.InterceptIncludeStrategy(Criteria, prev)); CurrentQueryable = Interceptors - .Where(t => t is IIncludeStrategyInterceptor) - .Cast>() - .Aggregate((IQueryable)CurrentQueryable, (prev, interceptor) => interceptor.InterceptIncludeStrategy(Criteria, prev)); + .Where(t => t is IIncludeStrategyInterceptor) + .Cast>() + .Aggregate((IQueryable)CurrentQueryable, (prev, interceptor) => interceptor.InterceptIncludeStrategy(Criteria, prev)); } - protected virtual void ApplyBeforeFilterInterceptors() + protected virtual void ApplyBeforeFilterInterceptors() { CurrentQueryable = Interceptors .Where(t => t is IBeforeQueryFilterInterceptor) @@ -355,12 +372,12 @@ namespace PoweredSoft.DynamicQuery .Aggregate(CurrentQueryable, (prev, interceptor) => interceptor.InterceptBeforeFiltering(Criteria, prev)); CurrentQueryable = Interceptors - .Where(t => t is IBeforeQueryFilterInterceptor) - .Cast>() - .Aggregate((IQueryable)CurrentQueryable, (prev, interceptor) => interceptor.InterceptBeforeFiltering(Criteria, prev)); + .Where(t => t is IBeforeQueryFilterInterceptor) + .Cast>() + .Aggregate((IQueryable)CurrentQueryable, (prev, interceptor) => interceptor.InterceptBeforeFiltering(Criteria, prev)); } - protected virtual List RecursiveRegroup(List groupRecords, List> aggregateResults, IGroup group, List> lastLists, List parentGroupResults = null) + protected virtual List> RecursiveRegroup(List groupRecords, List> aggregateResults, IGroup group, List<(List entities, IGroupQueryResult group)> lastLists, List> parentGroupResults = null) { var groupIndex = Criteria.Groups.IndexOf(group); var isLast = Criteria.Groups.Last() == group; @@ -371,13 +388,13 @@ namespace PoweredSoft.DynamicQuery .GroupBy(gk => gk.GetDynamicPropertyValue($"Key_{groupIndex}")) .Select(t => { - var groupResult = new GroupQueryResult(); + var groupResult = new GroupQueryResult(); // group results. - List groupResults; + List> groupResults; if (parentGroupResults == null) - groupResults = new List { groupResult }; + groupResults = new List> { groupResult }; else groupResults = parentGroupResults.Union(new[] { groupResult }).ToList(); @@ -406,41 +423,43 @@ namespace PoweredSoft.DynamicQuery if (isLast) { - var entities = t.SelectMany(t2 => t2.GetDynamicPropertyValue>("Records")).ToList(); - groupResult.Data = entities.Cast().ToList(); - lastLists.Add(groupResult.Data); + var entities = t.SelectMany(t2 => t2.GetDynamicPropertyValue>("Records")).ToList(); + var tuple = (entities, groupResult); + groupResult.Data = new List(); + lastLists.Add(tuple); } else { - groupResult.Data = RecursiveRegroup(t.ToList(), aggregateResults, Criteria.Groups[groupIndex + 1], lastLists, groupResults); + groupResult.SubGroups = RecursiveRegroup(t.ToList(), aggregateResults, Criteria.Groups[groupIndex + 1], lastLists, groupResults); } return groupResult; }) - .AsEnumerable() + .AsEnumerable>() .ToList(); + return ret; } - protected virtual async Task QueryInterceptToGrouped(List> lists) + protected virtual async Task QueryInterceptToGrouped(List<(List entities, IGroupQueryResult group)> lists) { - var entities = lists.SelectMany(t => t).Cast().ToList(); + var entities = lists.SelectMany(t => t.entities).ToList(); await AfterEntityReadInterceptors(entities); - var pairs = new List>(); + var pairs = new List>(); lists.ForEach(innerList => { - for(var i = 0; i < innerList.Count; i++) + for (var i = 0; i < innerList.entities.Count; i++) { - var entity = (T)innerList[i]; - var convertedObject = InterceptConvertToObject(entity); - innerList[i] = convertedObject; + var entity = innerList.entities[i]; + var convertedObject = InterceptConvertToObject(entity); + innerList.group.Data.Add(convertedObject); pairs.Add(Tuple.Create(entity, convertedObject)); } }); - await AfterReadInterceptors(pairs); + await AfterReadInterceptors(pairs); } } } diff --git a/PoweredSoft.DynamicQuery/QueryResult.cs b/PoweredSoft.DynamicQuery/QueryResult.cs index e4db5ab..9807aee 100644 --- a/PoweredSoft.DynamicQuery/QueryResult.cs +++ b/PoweredSoft.DynamicQuery/QueryResult.cs @@ -19,25 +19,36 @@ namespace PoweredSoft.DynamicQuery } // part of a result. - public abstract class QueryResult : IQueryResult + public abstract class QueryResult : IQueryResult { public List Aggregates { get; set; } - public List Data { get; set; } + public List Data { get; set; } public bool ShouldSerializeAggregates() => Aggregates != null; + + public bool ShouldSerializeData() => Data != null; } // just result - public class QueryExecutionResult : QueryResult, IQueryExecutionResult + public class QueryExecutionResult : QueryResult, IQueryExecutionResult { public long TotalRecords { get; set; } public long? NumberOfPages { get; set; } } + public class QueryExecutionGroupResult : QueryExecutionResult, IQueryExecutionGroupResult + { + public List> Groups { get; set; } + } + // group result. - public class GroupQueryResult : QueryResult, IGroupQueryResult + public class GroupQueryResult : QueryResult, IGroupQueryResult { public string GroupPath { get; set; } public object GroupValue { get; set; } + public bool HasSubGroups => SubGroups != null && SubGroups.Count > 1; + public List> SubGroups { get; set; } + + public bool ShouldSerializeSubGroups() => HasSubGroups; } } diff --git a/README.md b/README.md index 36cad35..86a1210 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,11 @@ It's a library that allows you to easily query a queryable using a criteria obje It also offers, to intercept the query using **IQueryInterceptor** implementations. +## Breaking Changes + +If you are moving up from v1, the breaking changes details are lower. + + ## Getting Started > Install nuget package to your awesome project. @@ -40,7 +45,7 @@ public class Startup ```csharp [HttpGet] -public IQueryExecutionResult Get( +public IQueryExecutionResult Get( [FromServices]YourContext context, [FromServices]IQueryHandler handler, [FromServices]IQueryCriteria criteria, @@ -55,7 +60,7 @@ public IQueryExecutionResult Get( } [HttpPost] -public IQueryExecutionResult Read( +public IQueryExecutionResult Read( [FromServices]YourContext context, [FromServices]IQueryHandler handler, [FromBody]IQueryCriteria criteria) @@ -70,7 +75,7 @@ public IQueryExecutionResult Read( ```csharp [HttpPost] -public async Task Read( +public async Task> Read( [FromServices]YourContext context, [FromServices]IQueryHandlerAsync handler, [FromBody]IQueryCriteria criteria) @@ -85,6 +90,25 @@ public async Task Read( Visit: https://github.com/PoweredSoft/DynamicQueryAspNetCoreSample +### Breaking Changes if you are migrating from 1.x + +Response interface, is now generic ```IQueryResult``` which impacts the way to execute the handler. + +#### Grouping results + +Since the results are now generic, it's no longer a List in the response so that changes the result if grouping is requested. + +You have now a property Groups, and HasSubGroups, and SubGroups. + +#### QueryConvertTo Interceptor + +If you are using IQueryConvertTo interceptors, it's new that you must specify the type you are converting to +Ex: +```csharp +IQueryable query = context.Somethings; +var result = handler.Execute(query, criteria); +``` + ## Criteria Criteria must implement the following interfaces @@ -114,13 +138,15 @@ var criteria = new QueryCriteria }; var queryHandler = new QueryHandler(); -IQueryExecutionResult result = queryHandler.Execute(someQueryable, criteria); +IQueryExecutionResult result = queryHandler.Execute(someQueryable, criteria); ``` ## Query Result Here is the interfaces that represent the result of query handling execution. +> Changed in 2.x + ```csharp public interface IAggregateResult { @@ -129,23 +155,35 @@ public interface IAggregateResult object Value { get; set; } } -public interface IQueryResult +public interface IQueryResult { List Aggregates { get; } - List Data { get; } + List Data { get; } } -public interface IGroupQueryResult : IQueryResult +public interface IGroupQueryResult : IQueryResult { string GroupPath { get; set; } object GroupValue { get; set; } + bool HasSubGroups { get; } + List> SubGroups { get; set; } } -public interface IQueryExecutionResult : IQueryResult +public interface IQueryExecutionResultPaging { long TotalRecords { get; set; } long? NumberOfPages { get; set; } } + +public interface IQueryExecutionResult : IQueryResult, IQueryExecutionResultPaging +{ + +} + +public interface IQueryExecutionGroupResult : IQueryExecutionResult +{ + List> Groups { get; set; } +} ``` @@ -180,4 +218,5 @@ IAggregateInterceptor | [interface](../master/PoweredSoft.DynamicQuery.Core/IAgg Interceptor | Interface | Example | Description ----------------------------------|---------------------------------------------------------------------------------------|-------------------------------------------------------------|------------------------------------------------------------------------------------------------ IQueryConvertInterceptor | [interface](../master/PoweredSoft.DynamicQuery.Core/IQueryConvertInterceptor.cs) | [test](../master/PoweredSoft.DynamicQuery.Test/ConvertibleInterceptorTests.cs) | This interceptor allows you to replace the object that is being returned by the query, by another object instance -IQueryConvertInterceptor<T> | [interface](../master/PoweredSoft.DynamicQuery.Core/IQueryConvertInterceptor.cs) | [test](../master/PoweredSoft.DynamicQuery.Test/ConvertibleInterceptorTests.cs#L72) | This interceptor allows you to replace the object that is being returned by the query, by another object instance +IQueryConvertInterceptor<T, T2> | [interface](../master/PoweredSoft.DynamicQuery.Core/IQueryConvertInterceptor.cs) | [test](../master/PoweredSoft.DynamicQuery.Test/ConvertibleInterceptorTests.cs#L72) | This interceptor allows you to replace the object that is being returned by the query, by another object instance **(restricts the source)** +IQueryConvertInterceptor<T, T2> | [interface](../master/PoweredSoft.DynamicQuery.Core/IQueryConvertInterceptor.cs) | [test](../master/PoweredSoft.DynamicQuery.Test/ConvertibleInterceptorTests.cs#L101) | This interceptor allows you to replace the object that is being returned by the query, by another object instance **(restricts the source & output)** \ No newline at end of file