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