execution options.
This commit is contained in:
		
							parent
							
								
									3d58f0496d
								
							
						
					
					
						commit
						9c0a05b579
					
				| @ -0,0 +1,12 @@ | |||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | 
 | ||||||
|  | namespace PoweredSoft.DynamicQuery.Core | ||||||
|  | { | ||||||
|  |     public interface IQueryExecutionOptionsInterceptor : IQueryInterceptor | ||||||
|  |     { | ||||||
|  |         IQueryExecutionOptions InterceptQueryExecutionOptions(IQueryable queryable, IQueryExecutionOptions current); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								PoweredSoft.DynamicQuery.Core/IQueryExecutionOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								PoweredSoft.DynamicQuery.Core/IQueryExecutionOptions.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | namespace PoweredSoft.DynamicQuery.Core | ||||||
|  | { | ||||||
|  |     public interface IQueryExecutionOptions | ||||||
|  |     { | ||||||
|  |         bool GroupByInMemory { get; set; } | ||||||
|  |         bool GroupByInMemoryNullCheck { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -15,11 +15,15 @@ namespace PoweredSoft.DynamicQuery.Core | |||||||
|     { |     { | ||||||
|         IQueryExecutionResult<TSource> Execute<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria); |         IQueryExecutionResult<TSource> Execute<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria); | ||||||
|         IQueryExecutionResult<TRecord> Execute<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria); |         IQueryExecutionResult<TRecord> Execute<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria); | ||||||
|  |         IQueryExecutionResult<TSource> Execute<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria, IQueryExecutionOptions options); | ||||||
|  |         IQueryExecutionResult<TRecord> Execute<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria, IQueryExecutionOptions options); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public interface IQueryHandlerAsync : IInterceptableQueryHandler |     public interface IQueryHandlerAsync : IInterceptableQueryHandler | ||||||
|     { |     { | ||||||
|         Task<IQueryExecutionResult<TSource>> ExecuteAsync<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)); |         Task<IQueryExecutionResult<TSource>> ExecuteAsync<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default); | ||||||
|         Task<IQueryExecutionResult<TRecord>> ExecuteAsync<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)); |         Task<IQueryExecutionResult<TRecord>> ExecuteAsync<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default); | ||||||
|  |         Task<IQueryExecutionResult<TSource>> ExecuteAsync<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria, IQueryExecutionOptions options, CancellationToken cancellationToken = default); | ||||||
|  |         Task<IQueryExecutionResult<TRecord>> ExecuteAsync<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria, IQueryExecutionOptions options, CancellationToken cancellationToken = default); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								PoweredSoft.DynamicQuery.Core/QueryExecutionOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								PoweredSoft.DynamicQuery.Core/QueryExecutionOptions.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | namespace PoweredSoft.DynamicQuery.Core | ||||||
|  | { | ||||||
|  |     public class QueryExecutionOptions : IQueryExecutionOptions | ||||||
|  |     { | ||||||
|  |         public bool GroupByInMemory { get; set; } = false; | ||||||
|  |         public bool GroupByInMemoryNullCheck { get; set; } = false; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,4 +1,5 @@ | |||||||
| using PoweredSoft.DynamicQuery.Core; | using Microsoft.EntityFrameworkCore; | ||||||
|  | using PoweredSoft.DynamicQuery.Core; | ||||||
| using PoweredSoft.DynamicQuery.Test.Mock; | using PoweredSoft.DynamicQuery.Test.Mock; | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| @ -14,7 +15,7 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|         { |         { | ||||||
|             public IAggregate InterceptAggregate(IAggregate aggregate) => new Aggregate |             public IAggregate InterceptAggregate(IAggregate aggregate) => new Aggregate | ||||||
|             { |             { | ||||||
|                 Path = "Item.Price", |                 Path = "Price", | ||||||
|                 Type = AggregateType.Avg |                 Type = AggregateType.Avg | ||||||
|             }; |             }; | ||||||
|         } |         } | ||||||
| @ -24,9 +25,11 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|         { |         { | ||||||
|             MockContextFactory.SeedAndTestContextFor("AggregatorInterceptorTests_Simple", TestSeeders.SimpleSeedScenario, ctx => |             MockContextFactory.SeedAndTestContextFor("AggregatorInterceptorTests_Simple", TestSeeders.SimpleSeedScenario, ctx => | ||||||
|             { |             { | ||||||
|                 var expected = ctx.OrderItems.GroupBy(t => true).Select(t => new |                 var expected = ctx.Items | ||||||
|  |                     .GroupBy(t => true) | ||||||
|  |                     .Select(t => new | ||||||
|                     { |                     { | ||||||
|                     PriceAtTheTime = t.Average(t2 => t2.Item.Price) |                         PriceAtTheTime = t.Average(t2 => t2.Price) | ||||||
|                     }).First(); |                     }).First(); | ||||||
| 
 | 
 | ||||||
|                 var criteria = new QueryCriteria(); |                 var criteria = new QueryCriteria(); | ||||||
| @ -37,7 +40,7 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|                 }); |                 }); | ||||||
|                 var queryHandler = new QueryHandler(); |                 var queryHandler = new QueryHandler(); | ||||||
|                 queryHandler.AddInterceptor(new MockAggregateInterceptor()); |                 queryHandler.AddInterceptor(new MockAggregateInterceptor()); | ||||||
|                 var result = queryHandler.Execute(ctx.OrderItems, criteria); |                 var result = queryHandler.Execute(ctx.Items, criteria); | ||||||
|                 Assert.Equal(expected.PriceAtTheTime, result.Aggregates.First().Value); |                 Assert.Equal(expected.PriceAtTheTime, result.Aggregates.First().Value); | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| using PoweredSoft.DynamicQuery.Core; | using Microsoft.EntityFrameworkCore; | ||||||
|  | using PoweredSoft.DynamicQuery.Core; | ||||||
| using PoweredSoft.DynamicQuery.Test.Mock; | using PoweredSoft.DynamicQuery.Test.Mock; | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| @ -27,10 +28,11 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|                         ItemQuantityAverage = t.Average(t2 => t2.Quantity), |                         ItemQuantityAverage = t.Average(t2 => t2.Quantity), | ||||||
|                         ItemQuantitySum = t.Sum(t2 => t2.Quantity), |                         ItemQuantitySum = t.Sum(t2 => t2.Quantity), | ||||||
|                         AvgOfPrice = t.Average(t2 => t2.PriceAtTheTime), |                         AvgOfPrice = t.Average(t2 => t2.PriceAtTheTime), | ||||||
|  |                         /* not supported by ef core 3.0 | ||||||
|                         First = t.First(), |                         First = t.First(), | ||||||
|                         FirstOrDefault = t.FirstOrDefault(), |                         FirstOrDefault = t.FirstOrDefault(), | ||||||
|                         Last = t.Last(), |                         Last = t.Last(), | ||||||
|                         LastOrDefault = t.LastOrDefault() |                         LastOrDefault = t.LastOrDefault()*/ | ||||||
|                     }) |                     }) | ||||||
|                     .First(); |                     .First(); | ||||||
| 
 | 
 | ||||||
| @ -45,21 +47,28 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|                         new Aggregate { Type = AggregateType.Avg, Path = "PriceAtTheTime"}, |                         new Aggregate { Type = AggregateType.Avg, Path = "PriceAtTheTime"}, | ||||||
|                         new Aggregate { Type = AggregateType.Min, Path = "Quantity"}, |                         new Aggregate { Type = AggregateType.Min, Path = "Quantity"}, | ||||||
|                         new Aggregate { Type = AggregateType.Max, Path = "Quantity" }, |                         new Aggregate { Type = AggregateType.Max, Path = "Quantity" }, | ||||||
|  |                         /*not support by ef core 3.0 | ||||||
|                         new Aggregate { Type = AggregateType.First }, |                         new Aggregate { Type = AggregateType.First }, | ||||||
|                         new Aggregate { Type = AggregateType.FirstOrDefault }, |                         new Aggregate { Type = AggregateType.FirstOrDefault }, | ||||||
|                         new Aggregate { Type = AggregateType.Last }, |                         new Aggregate { Type = AggregateType.Last }, | ||||||
|                         new Aggregate { Type = AggregateType.LastOrDefault }, |                         new Aggregate { Type = AggregateType.LastOrDefault }, | ||||||
|  |                         */ | ||||||
|                     } |                     } | ||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
|                 var queryHandler = new QueryHandler(); |                 var queryHandler = new QueryHandler(); | ||||||
|                 var result = queryHandler.Execute(ctx.OrderItems, criteria); |                 var result = queryHandler.Execute(ctx.OrderItems, criteria, new QueryExecutionOptions | ||||||
|  |                 { | ||||||
|  |                     GroupByInMemory = true | ||||||
|  |                 }); | ||||||
| 
 | 
 | ||||||
|                 var aggCount = result.Aggregates.First(t => t.Type == AggregateType.Count); |                 var aggCount = result.Aggregates.First(t => t.Type == AggregateType.Count); | ||||||
|  | 
 | ||||||
|  |                 /* | ||||||
|                 var aggFirst = result.Aggregates.First(t => t.Type == AggregateType.First); |                 var aggFirst = result.Aggregates.First(t => t.Type == AggregateType.First); | ||||||
|                 var aggFirstOrDefault = result.Aggregates.First(t => t.Type == AggregateType.FirstOrDefault); |                 var aggFirstOrDefault = result.Aggregates.First(t => t.Type == AggregateType.FirstOrDefault); | ||||||
|                 var aggLast = result.Aggregates.First(t => t.Type == AggregateType.Last); |                 var aggLast = result.Aggregates.First(t => t.Type == AggregateType.Last); | ||||||
|                 var aggLastOrDefault = result.Aggregates.First(t => t.Type == AggregateType.LastOrDefault); |                 var aggLastOrDefault = result.Aggregates.First(t => t.Type == AggregateType.LastOrDefault);*/ | ||||||
| 
 | 
 | ||||||
|                 var aggItemQuantityMin  = result.Aggregates.First(t => t.Type == AggregateType.Min && t.Path == "Quantity"); |                 var aggItemQuantityMin  = result.Aggregates.First(t => t.Type == AggregateType.Min && t.Path == "Quantity"); | ||||||
|                 var aggItemQuantityMax = result.Aggregates.First(t => t.Type == AggregateType.Max && t.Path == "Quantity"); |                 var aggItemQuantityMax = result.Aggregates.First(t => t.Type == AggregateType.Max && t.Path == "Quantity"); | ||||||
| @ -68,10 +77,11 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|                 var aggAvgOfPrice = result.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "PriceAtTheTime"); |                 var aggAvgOfPrice = result.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "PriceAtTheTime"); | ||||||
| 
 | 
 | ||||||
|                 Assert.Equal(shouldResult.Count, aggCount.Value); |                 Assert.Equal(shouldResult.Count, aggCount.Value); | ||||||
|  |                 /* | ||||||
|                 Assert.Equal(shouldResult.First?.Id, (aggFirst.Value as OrderItem)?.Id); |                 Assert.Equal(shouldResult.First?.Id, (aggFirst.Value as OrderItem)?.Id); | ||||||
|                 Assert.Equal(shouldResult.FirstOrDefault?.Id, (aggFirstOrDefault.Value as OrderItem)?.Id); |                 Assert.Equal(shouldResult.FirstOrDefault?.Id, (aggFirstOrDefault.Value as OrderItem)?.Id); | ||||||
|                 Assert.Equal(shouldResult.Last?.Id, (aggLast.Value as OrderItem)?.Id); |                 Assert.Equal(shouldResult.Last?.Id, (aggLast.Value as OrderItem)?.Id); | ||||||
|                 Assert.Equal(shouldResult.LastOrDefault?.Id, (aggLastOrDefault.Value as OrderItem)?.Id); |                 Assert.Equal(shouldResult.LastOrDefault?.Id, (aggLastOrDefault.Value as OrderItem)?.Id);*/ | ||||||
| 
 | 
 | ||||||
|                 Assert.Equal(shouldResult.ItemQuantityAverage, aggItemQuantityAverage.Value); |                 Assert.Equal(shouldResult.ItemQuantityAverage, aggItemQuantityAverage.Value); | ||||||
|                 Assert.Equal(shouldResult.ItemQuantitySum, aggItemQuantitySum.Value); |                 Assert.Equal(shouldResult.ItemQuantitySum, aggItemQuantitySum.Value); | ||||||
| @ -113,7 +123,11 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
|                 var queryHandler = new QueryHandler(); |                 var queryHandler = new QueryHandler(); | ||||||
|                 var result = queryHandler.Execute(ctx.OrderItems, criteria); |                 var queryable = ctx.OrderItems.Include(t => t.Order); | ||||||
|  |                 var result = queryHandler.Execute(queryable, criteria, new QueryExecutionOptions | ||||||
|  |                 { | ||||||
|  |                     GroupByInMemory = true | ||||||
|  |                 }); | ||||||
| 
 | 
 | ||||||
|                 var groupedResult = result as IQueryExecutionGroupResult<OrderItem>; |                 var groupedResult = result as IQueryExecutionGroupResult<OrderItem>; | ||||||
|                 Assert.NotNull(groupedResult); |                 Assert.NotNull(groupedResult); | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| using PoweredSoft.Data; | using Microsoft.EntityFrameworkCore; | ||||||
|  | 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.Extensions; | ||||||
| @ -9,6 +10,7 @@ using System.Linq; | |||||||
| using System.Text; | using System.Text; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using Xunit; | using Xunit; | ||||||
|  | using static PoweredSoft.DynamicQuery.Test.GroupInterceptorTests; | ||||||
| 
 | 
 | ||||||
| namespace PoweredSoft.DynamicQuery.Test | namespace PoweredSoft.DynamicQuery.Test | ||||||
| { | { | ||||||
| @ -65,7 +67,11 @@ 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.Include(t => t.Order.Customer), criteria, new QueryExecutionOptions | ||||||
|  |                 { | ||||||
|  |                     GroupByInMemory = true | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|                 var groups = result.GroupedResult().Groups; |                 var groups = result.GroupedResult().Groups; | ||||||
| 
 | 
 | ||||||
|                 // validate group and aggregates of groups. |                 // validate group and aggregates of groups. | ||||||
| @ -165,6 +171,68 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|                 Assert.Equal(resultShouldMatch, result.Data); |                 Assert.Equal(resultShouldMatch, result.Data); | ||||||
|             }); |             }); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         [Fact] | ||||||
|  |         public void WithGroupingInterceptorOptions() | ||||||
|  |         { | ||||||
|  |             MockContextFactory.SeedAndTestContextFor("AsyncTests_WithGroupingInterceptorOptions", TestSeeders.SimpleSeedScenario, async ctx => | ||||||
|  |             { | ||||||
|  |                 var shouldResults = ctx.OrderItems | ||||||
|  |                     .GroupBy(t => t.Order.CustomerId) | ||||||
|  |                     .Select(t => new | ||||||
|  |                     { | ||||||
|  |                         GroupValue = t.Key, | ||||||
|  |                         Count = t.Count(), | ||||||
|  |                         ItemQuantityAverage = t.Average(t2 => t2.Quantity), | ||||||
|  |                         ItemQuantitySum = t.Sum(t2 => t2.Quantity), | ||||||
|  |                         AvgOfPrice = t.Average(t2 => t2.PriceAtTheTime) | ||||||
|  |                     }) | ||||||
|  |                     .ToList(); | ||||||
|  | 
 | ||||||
|  |                 // query handler that is empty should be the same as running to list. | ||||||
|  |                 var criteria = new QueryCriteria() | ||||||
|  |                 { | ||||||
|  |                     Groups = new List<IGroup> | ||||||
|  |                     { | ||||||
|  |                         new Group { Path = "Order.CustomerId" } | ||||||
|  |                     }, | ||||||
|  |                     Aggregates = new List<Core.IAggregate> | ||||||
|  |                     { | ||||||
|  |                         new Aggregate { Type = AggregateType.Count }, | ||||||
|  |                         new Aggregate { Type = AggregateType.Avg, Path = "Quantity" }, | ||||||
|  |                         new Aggregate { Type = AggregateType.Sum, Path = "Quantity" }, | ||||||
|  |                         new Aggregate { Type = AggregateType.Avg, Path = "PriceAtTheTime"} | ||||||
|  |                     } | ||||||
|  |                 }; | ||||||
|  |                 var asyncService = new AsyncQueryableService(new[] { new AsyncQueryableHandlerService() }); | ||||||
|  |                 var queryHandler = new QueryHandlerAsync(asyncService); | ||||||
|  |                 queryHandler.AddInterceptor(new MockQueryExecutionOptionsInterceptor()); | ||||||
|  |                 var result = await queryHandler.ExecuteAsync(ctx.OrderItems.Include(t => t.Order.Customer), criteria); | ||||||
|  | 
 | ||||||
|  |                 var groups = result.GroupedResult().Groups; | ||||||
|  | 
 | ||||||
|  |                 // validate group and aggregates of groups. | ||||||
|  |                 Assert.Equal(groups.Count, shouldResults.Count); | ||||||
|  |                 Assert.All(groups, g => | ||||||
|  |                 { | ||||||
|  |                     var index = groups.IndexOf(g); | ||||||
|  |                     var shouldResult = shouldResults[index]; | ||||||
|  | 
 | ||||||
|  |                     // validate the group value. | ||||||
|  |                     Assert.Equal(g.GroupValue, shouldResult.GroupValue); | ||||||
|  | 
 | ||||||
|  |                     // validate the group aggregates. | ||||||
|  |                     var aggCount = g.Aggregates.First(t => t.Type == AggregateType.Count); | ||||||
|  |                     var aggItemQuantityAverage = g.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "Quantity"); | ||||||
|  |                     var aggItemQuantitySum = g.Aggregates.First(t => t.Type == AggregateType.Sum && t.Path == "Quantity"); | ||||||
|  |                     var aggAvgOfPrice = g.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "PriceAtTheTime"); | ||||||
|  |                     Assert.Equal(shouldResult.Count, aggCount.Value); | ||||||
|  |                     Assert.Equal(shouldResult.ItemQuantityAverage, aggItemQuantityAverage.Value); | ||||||
|  |                     Assert.Equal(shouldResult.ItemQuantitySum, aggItemQuantitySum.Value); | ||||||
|  |                     Assert.Equal(shouldResult.AvgOfPrice, aggAvgOfPrice.Value); | ||||||
|  |                 }); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| using PoweredSoft.DynamicQuery.Core; | using Microsoft.EntityFrameworkCore; | ||||||
|  | using PoweredSoft.DynamicQuery.Core; | ||||||
| using PoweredSoft.DynamicQuery.Extensions; | using PoweredSoft.DynamicQuery.Extensions; | ||||||
| using PoweredSoft.DynamicQuery.Test.Mock; | using PoweredSoft.DynamicQuery.Test.Mock; | ||||||
| using System; | using System; | ||||||
| @ -9,7 +10,7 @@ using Xunit; | |||||||
| 
 | 
 | ||||||
| namespace PoweredSoft.DynamicQuery.Test | namespace PoweredSoft.DynamicQuery.Test | ||||||
| { | { | ||||||
|     public class GroupInterceptorTests |     public partial class GroupInterceptorTests | ||||||
|     { |     { | ||||||
|         private class MockGroupInterceptor : IGroupInterceptor |         private class MockGroupInterceptor : IGroupInterceptor | ||||||
|         { |         { | ||||||
| @ -37,7 +38,34 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|                 criteria.Groups.Add(new Group { Path = "CustomerFirstName" }); |                 criteria.Groups.Add(new Group { Path = "CustomerFirstName" }); | ||||||
|                 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.Include(t => t.Customer), criteria, new QueryExecutionOptions | ||||||
|  |                 { | ||||||
|  |                     GroupByInMemory = true | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |                 var groupedResult = result.GroupedResult(); | ||||||
|  |                 var actual = groupedResult.Groups.Select(t => t.GroupValue).ToList(); | ||||||
|  |                 Assert.Equal(expected, actual); | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         [Fact] | ||||||
|  |         public void WithInterptorSimple() | ||||||
|  |         { | ||||||
|  |             MockContextFactory.SeedAndTestContextFor("GroupInterceptorTests_Simple", TestSeeders.SimpleSeedScenario, ctx => | ||||||
|  |             { | ||||||
|  |                 var expected = ctx.Orders | ||||||
|  |                     .OrderBy(t => t.Customer.FirstName) | ||||||
|  |                     .GroupBy(t => t.Customer.FirstName) | ||||||
|  |                     .Select(t => t.Key) | ||||||
|  |                     .ToList(); | ||||||
|  | 
 | ||||||
|  |                 var criteria = new QueryCriteria(); | ||||||
|  |                 criteria.Groups.Add(new Group { Path = "CustomerFirstName" }); | ||||||
|  |                 var queryHandler = new QueryHandler(); | ||||||
|  |                 queryHandler.AddInterceptor(new MockGroupInterceptor()); | ||||||
|  |                 queryHandler.AddInterceptor(new MockQueryExecutionOptionsInterceptor()); | ||||||
|  |                 var result = queryHandler.Execute(ctx.Orders.Include(t => t.Customer), criteria); | ||||||
| 
 | 
 | ||||||
|                 var groupedResult = result.GroupedResult(); |                 var groupedResult = result.GroupedResult(); | ||||||
|                 var actual = groupedResult.Groups.Select(t => t.GroupValue).ToList(); |                 var actual = groupedResult.Groups.Select(t => t.GroupValue).ToList(); | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| using PoweredSoft.DynamicQuery.Core; | using Microsoft.EntityFrameworkCore; | ||||||
|  | using PoweredSoft.DynamicQuery.Core; | ||||||
| using PoweredSoft.DynamicQuery.Extensions; | using PoweredSoft.DynamicQuery.Extensions; | ||||||
| using PoweredSoft.DynamicQuery.Test.Mock; | using PoweredSoft.DynamicQuery.Test.Mock; | ||||||
| using System; | using System; | ||||||
| @ -18,23 +19,32 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|         { |         { | ||||||
|             MockContextFactory.SeedAndTestContextFor("GroupTests_Simple", TestSeeders.SimpleSeedScenario, ctx => |             MockContextFactory.SeedAndTestContextFor("GroupTests_Simple", TestSeeders.SimpleSeedScenario, ctx => | ||||||
|             { |             { | ||||||
|                 var shouldResult = ctx.Orders.OrderBy(t => t.Customer).GroupBy(t => t.Customer).Select(t => new |                 var shouldResult = ctx.Orders | ||||||
|  |                     .OrderBy(t => t.CustomerId) | ||||||
|  |                     .ToList() | ||||||
|  |                     .GroupBy(t => t.CustomerId) | ||||||
|  |                     .Select(t => new | ||||||
|                     { |                     { | ||||||
|                     Customer = t.Key, |                         CustomerId = t.Key, | ||||||
|                         Orders = t.ToList() |                         Orders = t.ToList() | ||||||
|                 }).ToList(); |                     }) | ||||||
|  |                     .ToList(); | ||||||
| 
 | 
 | ||||||
|                 // query handler that is empty should be the same as running to list. |                 // query handler that is empty should be the same as running to list. | ||||||
|                 var criteria = new QueryCriteria() |                 var criteria = new QueryCriteria() | ||||||
|                 { |                 { | ||||||
|                     Groups = new List<IGroup> |                     Groups = new List<IGroup> | ||||||
|                     { |                     { | ||||||
|                         new Group { Path = "Customer" } |                         new Group { Path = "CustomerId" } | ||||||
|                     } |                     } | ||||||
|                 }; |                 }; | ||||||
| 
 | 
 | ||||||
|                 var queryHandler = new QueryHandler(); |                 var queryHandler = new QueryHandler(); | ||||||
|                 var result = queryHandler.Execute(ctx.Orders, criteria); |                 var result = queryHandler.Execute(ctx.Orders, criteria, new QueryExecutionOptions | ||||||
|  |                 { | ||||||
|  |                     GroupByInMemory = true, | ||||||
|  |                     GroupByInMemoryNullCheck = false | ||||||
|  |                 }); | ||||||
|                 var groupedResult = result.GroupedResult(); |                 var groupedResult = result.GroupedResult(); | ||||||
| 
 | 
 | ||||||
|                 // top level should have same amount of group levels. |                 // top level should have same amount of group levels. | ||||||
| @ -43,7 +53,7 @@ namespace PoweredSoft.DynamicQuery.Test | |||||||
|                 { |                 { | ||||||
|                     var expected = shouldResult[0]; |                     var expected = shouldResult[0]; | ||||||
|                     var actual = groupedResult.Groups[0]; |                     var actual = groupedResult.Groups[0]; | ||||||
|                     Assert.Equal(expected.Customer.Id, (actual.GroupValue as Customer).Id); |                     Assert.Equal(expected.CustomerId, actual.GroupValue); | ||||||
| 
 | 
 | ||||||
|                     var expectedOrderIds = expected.Orders.Select(t => t.Id).ToList(); |                     var expectedOrderIds = expected.Orders.Select(t => t.Id).ToList(); | ||||||
|                     var actualOrderIds = actual.Data.Cast<Order>().Select(t => t.Id).ToList(); |                     var actualOrderIds = actual.Data.Cast<Order>().Select(t => t.Id).ToList(); | ||||||
| @ -71,7 +81,10 @@ 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, new QueryExecutionOptions | ||||||
|  |                 { | ||||||
|  |                     GroupByInMemory = true | ||||||
|  |                 }); | ||||||
| 
 | 
 | ||||||
|                 var groupedResult = result.GroupedResult(); |                 var groupedResult = result.GroupedResult(); | ||||||
| 
 | 
 | ||||||
| @ -106,7 +119,11 @@ 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<Ticket, InterceptorWithGroupingFakeModel>(ctx.Tickets, criteria); |                 var result = queryHandler.Execute<Ticket, InterceptorWithGroupingFakeModel>(ctx.Tickets, criteria, new QueryExecutionOptions | ||||||
|  |                 { | ||||||
|  |                     GroupByInMemory = true | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|                 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); | ||||||
|  | |||||||
| @ -14,12 +14,9 @@ namespace PoweredSoft.DynamicQuery.Test.Mock | |||||||
|         public static void TestContextFor(string testName, Action<MockContext> action) |         public static void TestContextFor(string testName, Action<MockContext> action) | ||||||
|         { |         { | ||||||
|             var options = new DbContextOptionsBuilder<MockContext>() |             var options = new DbContextOptionsBuilder<MockContext>() | ||||||
|                 .ConfigureWarnings(warnings =>  |  | ||||||
|                     warnings.Ignore(RelationalEventId.QueryClientEvaluationWarning) |  | ||||||
|                 ) |  | ||||||
|                 .UseInMemoryDatabase(databaseName: testName).Options; |                 .UseInMemoryDatabase(databaseName: testName).Options; | ||||||
| 
 | 
 | ||||||
|             using (var ctx = new MockContext(options)) |             using var ctx = new MockContext(options); | ||||||
|                 action(ctx); |                 action(ctx); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -0,0 +1,22 @@ | |||||||
|  | using Microsoft.EntityFrameworkCore.Query.Internal; | ||||||
|  | using PoweredSoft.DynamicQuery.Core; | ||||||
|  | using System.Linq; | ||||||
|  | 
 | ||||||
|  | namespace PoweredSoft.DynamicQuery.Test | ||||||
|  | { | ||||||
|  |     public partial class GroupInterceptorTests | ||||||
|  |     { | ||||||
|  |         public class MockQueryExecutionOptionsInterceptor : IQueryExecutionOptionsInterceptor | ||||||
|  |         { | ||||||
|  |             public IQueryExecutionOptions InterceptQueryExecutionOptions(IQueryable queryable, IQueryExecutionOptions current) | ||||||
|  |             { | ||||||
|  |                 if (queryable.Provider is IAsyncQueryProvider) | ||||||
|  |                 { | ||||||
|  |                     current.GroupByInMemory = true; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 return current; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -8,12 +8,12 @@ | |||||||
| 
 | 
 | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <PackageReference Include="Bogus" Version="28.3.2" /> |     <PackageReference Include="Bogus" Version="28.3.2" /> | ||||||
|     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" /> |     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" /> | ||||||
|     <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="2.2.6" /> |     <PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.0.0" /> | ||||||
|     <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="2.2.6" /> |     <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.0.0" /> | ||||||
|     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" /> |     <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" /> | ||||||
|     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" /> |     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" /> | ||||||
|     <PackageReference Include="PoweredSoft.Data.EntityFrameworkCore" Version="1.1.3" /> |     <PackageReference Include="PoweredSoft.Data.EntityFrameworkCore" Version="2.0.0" /> | ||||||
|     <PackageReference Include="xunit" Version="2.4.1" /> |     <PackageReference Include="xunit" Version="2.4.1" /> | ||||||
|     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> |     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1"> | ||||||
|       <PrivateAssets>all</PrivateAssets> |       <PrivateAssets>all</PrivateAssets> | ||||||
|  | |||||||
| @ -43,14 +43,23 @@ namespace PoweredSoft.DynamicQuery | |||||||
|             ApplySorting<TSource>(); |             ApplySorting<TSource>(); | ||||||
|             ApplyPaging<TSource>(); |             ApplyPaging<TSource>(); | ||||||
| 
 | 
 | ||||||
|             // create group & select expression. |             if (Options.GroupByInMemory) | ||||||
|             CurrentQueryable = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb => finalGroups.ForEach((fg, index) => gb.Path(fg.Path, $"Key_{index}"))); |                 CurrentQueryable = CurrentQueryable.ToObjectList().Cast<TSource>().AsQueryable();  | ||||||
|  | 
 | ||||||
|  |             CurrentQueryable = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb => | ||||||
|  |             { | ||||||
|  |                 gb.NullChecking(Options.GroupByInMemory ? Options.GroupByInMemoryNullCheck : false); | ||||||
|  |                 finalGroups.ForEach((fg, index) => gb.Path(fg.Path, $"Key_{index}")); | ||||||
|  |             }); | ||||||
|  | 
 | ||||||
|             CurrentQueryable = CurrentQueryable.Select(sb => |             CurrentQueryable = CurrentQueryable.Select(sb => | ||||||
|             { |             { | ||||||
|  |                 sb.NullChecking(Options.GroupByInMemory ? Options.GroupByInMemoryNullCheck : false); | ||||||
|                 finalGroups.ForEach((fg, index) => sb.Key($"Key_{index}", $"Key_{index}")); |                 finalGroups.ForEach((fg, index) => sb.Key($"Key_{index}", $"Key_{index}")); | ||||||
|                 sb.ToList("Records"); |                 sb.ToList("Records"); | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|             // loop through the grouped records. |             // loop through the grouped records. | ||||||
|             var groupRecords = CurrentQueryable.ToDynamicClassList(); |             var groupRecords = CurrentQueryable.ToDynamicClassList(); | ||||||
| 
 | 
 | ||||||
| @ -118,13 +127,25 @@ namespace PoweredSoft.DynamicQuery | |||||||
| 
 | 
 | ||||||
|         public IQueryExecutionResult<TSource> Execute<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria) |         public IQueryExecutionResult<TSource> Execute<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria) | ||||||
|         { |         { | ||||||
|             Reset(queryable, criteria); |             Reset(queryable, criteria, new QueryExecutionOptions()); | ||||||
|             return FinalExecute<TSource, TSource>(); |             return FinalExecute<TSource, TSource>(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public IQueryExecutionResult<TRecord> Execute<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria) |         public IQueryExecutionResult<TRecord> Execute<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria) | ||||||
|         { |         { | ||||||
|             Reset(queryable, criteria); |             Reset(queryable, criteria, new QueryExecutionOptions()); | ||||||
|  |             return FinalExecute<TSource, TRecord>(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public IQueryExecutionResult<TSource> Execute<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria, IQueryExecutionOptions options) | ||||||
|  |         { | ||||||
|  |             Reset(queryable, criteria, options); | ||||||
|  |             return FinalExecute<TSource, TSource>(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public IQueryExecutionResult<TRecord> Execute<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria, IQueryExecutionOptions options) | ||||||
|  |         { | ||||||
|  |             Reset(queryable, criteria, options); | ||||||
|             return FinalExecute<TSource, TRecord>(); |             return FinalExecute<TSource, TRecord>(); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ namespace PoweredSoft.DynamicQuery | |||||||
|             AsyncQueryableService = asyncQueryableService; |             AsyncQueryableService = asyncQueryableService; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         protected virtual Task<IQueryExecutionResult<TRecord>> FinalExecuteAsync<TSource, TRecord>(CancellationToken cancellationToken = default(CancellationToken)) |         protected virtual Task<IQueryExecutionResult<TRecord>> FinalExecuteAsync<TSource, TRecord>(CancellationToken cancellationToken = default) | ||||||
|         { |         { | ||||||
|             CommonBeforeExecute<TSource>(); |             CommonBeforeExecute<TSource>(); | ||||||
|             return HasGrouping ? ExecuteAsyncGrouping<TSource, TRecord>(cancellationToken) : ExecuteAsyncNoGrouping<TSource, TRecord>(cancellationToken); |             return HasGrouping ? ExecuteAsyncGrouping<TSource, TRecord>(cancellationToken) : ExecuteAsyncNoGrouping<TSource, TRecord>(cancellationToken); | ||||||
| @ -50,8 +50,35 @@ namespace PoweredSoft.DynamicQuery | |||||||
|             ApplySorting<TSource>(); |             ApplySorting<TSource>(); | ||||||
|             ApplyPaging<TSource>(); |             ApplyPaging<TSource>(); | ||||||
| 
 | 
 | ||||||
|  |             List<DynamicClass> groupRecords; | ||||||
|  | 
 | ||||||
|  |             if (Options.GroupByInMemory) | ||||||
|  |             { | ||||||
|  |                 CurrentQueryable = CurrentQueryable.ToObjectList().Cast<TSource>().AsQueryable(); | ||||||
|  | 
 | ||||||
|                 // 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 => | ||||||
|  |                 { | ||||||
|  |                     gb.NullChecking(Options.GroupByInMemory ? Options.GroupByInMemoryNullCheck : false); | ||||||
|  |                     finalGroups.ForEach((fg, index) => gb.Path(fg.Path, $"Key_{index}")); | ||||||
|  |                 }); | ||||||
|  |                 CurrentQueryable = CurrentQueryable.Select(sb => | ||||||
|  |                 { | ||||||
|  |                     sb.NullChecking(Options.GroupByInMemory ? Options.GroupByInMemoryNullCheck : false); | ||||||
|  |                     finalGroups.ForEach((fg, index) => sb.Key($"Key_{index}", $"Key_{index}")); | ||||||
|  |                     sb.ToList("Records"); | ||||||
|  |                 }); | ||||||
|  | 
 | ||||||
|  |                 // loop through the grouped records. | ||||||
|  |                 groupRecords = CurrentQueryable.Cast<DynamicClass>().ToList(); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             {  | ||||||
|  |                 // create group & select expression. | ||||||
|  |                 CurrentQueryable = CurrentQueryable.GroupBy(QueryableUnderlyingType, gb => | ||||||
|  |                 { | ||||||
|  |                     finalGroups.ForEach((fg, index) => gb.Path(fg.Path, $"Key_{index}")); | ||||||
|  |                 }); | ||||||
|                 CurrentQueryable = CurrentQueryable.Select(sb => |                 CurrentQueryable = CurrentQueryable.Select(sb => | ||||||
|                 { |                 { | ||||||
|                     finalGroups.ForEach((fg, index) => sb.Key($"Key_{index}", $"Key_{index}")); |                     finalGroups.ForEach((fg, index) => sb.Key($"Key_{index}", $"Key_{index}")); | ||||||
| @ -59,7 +86,8 @@ namespace PoweredSoft.DynamicQuery | |||||||
|                 }); |                 }); | ||||||
| 
 | 
 | ||||||
|                 // loop through the grouped records. |                 // loop through the grouped records. | ||||||
|             var groupRecords = await AsyncQueryableService.ToListAsync(CurrentQueryable.Cast<DynamicClass>(), cancellationToken); |                 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<TSource> entities, IGroupQueryResult<TRecord> group)>(); |             var lastLists = new List<(List<TSource> entities, IGroupQueryResult<TRecord> group)>(); | ||||||
| @ -127,15 +155,27 @@ namespace PoweredSoft.DynamicQuery | |||||||
|             return finalResult; |             return finalResult; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public Task<IQueryExecutionResult<TSource>> ExecuteAsync<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)) |         public Task<IQueryExecutionResult<TSource>> ExecuteAsync<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default) | ||||||
|         { |         { | ||||||
|             Reset(queryable, criteria); |             Reset(queryable, criteria, new QueryExecutionOptions()); | ||||||
|             return FinalExecuteAsync<TSource, TSource>(cancellationToken); |             return FinalExecuteAsync<TSource, TSource>(cancellationToken); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         public Task<IQueryExecutionResult<TRecord>> ExecuteAsync<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)) |         public Task<IQueryExecutionResult<TRecord>> ExecuteAsync<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default) | ||||||
|         { |         { | ||||||
|             Reset(queryable, criteria); |             Reset(queryable, criteria, new QueryExecutionOptions()); | ||||||
|  |             return FinalExecuteAsync<TSource, TRecord>(cancellationToken); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Task<IQueryExecutionResult<TSource>> ExecuteAsync<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria, IQueryExecutionOptions options, CancellationToken cancellationToken = default) | ||||||
|  |         { | ||||||
|  |             Reset(queryable, criteria, options); | ||||||
|  |             return FinalExecuteAsync<TSource, TSource>(cancellationToken); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Task<IQueryExecutionResult<TRecord>> ExecuteAsync<TSource, TRecord>(IQueryable<TSource> queryable, IQueryCriteria criteria, IQueryExecutionOptions options, CancellationToken cancellationToken = default) | ||||||
|  |         { | ||||||
|  |             Reset(queryable, criteria, options); | ||||||
|             return FinalExecuteAsync<TSource, TRecord>(cancellationToken); |             return FinalExecuteAsync<TSource, TRecord>(cancellationToken); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -20,24 +20,36 @@ namespace PoweredSoft.DynamicQuery | |||||||
|         protected IQueryCriteria Criteria { get; set; } |         protected IQueryCriteria Criteria { get; set; } | ||||||
|         protected IQueryable QueryableAtStart { get; private set; } |         protected IQueryable QueryableAtStart { get; private set; } | ||||||
|         protected IQueryable CurrentQueryable { get; set; } |         protected IQueryable CurrentQueryable { get; set; } | ||||||
|  |         protected IQueryExecutionOptions Options { get; private set; } | ||||||
|  | 
 | ||||||
|         protected Type QueryableUnderlyingType => QueryableAtStart.ElementType; |         protected Type QueryableUnderlyingType => QueryableAtStart.ElementType; | ||||||
|         protected bool HasGrouping => Criteria.Groups?.Any() == true; |         protected bool HasGrouping => Criteria.Groups?.Any() == true; | ||||||
|         protected bool HasPaging => Criteria.PageSize.HasValue && Criteria.PageSize > 0; |         protected bool HasPaging => Criteria.PageSize.HasValue && Criteria.PageSize > 0; | ||||||
| 
 | 
 | ||||||
|         protected virtual void Reset(IQueryable queryable, IQueryCriteria criteria) |         protected virtual void Reset(IQueryable queryable, IQueryCriteria criteria, IQueryExecutionOptions options) | ||||||
|         { |         { | ||||||
|             Criteria = criteria ?? throw new ArgumentNullException("criteria"); |             Criteria = criteria ?? throw new ArgumentNullException("criteria"); | ||||||
|             QueryableAtStart = queryable ?? throw new ArgumentNullException("queryable"); |             QueryableAtStart = queryable ?? throw new ArgumentNullException("queryable"); | ||||||
|             CurrentQueryable = QueryableAtStart; |             CurrentQueryable = QueryableAtStart; | ||||||
|  |             Options = options; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         protected virtual void CommonBeforeExecute<TSource>() |         protected virtual void CommonBeforeExecute<TSource>() | ||||||
|         { |         { | ||||||
|  |             ApplyQueryExecutionOptionIncerceptors(); | ||||||
|             ApplyIncludeStrategyInterceptors<TSource>(); |             ApplyIncludeStrategyInterceptors<TSource>(); | ||||||
|             ApplyBeforeFilterInterceptors<TSource>(); |             ApplyBeforeFilterInterceptors<TSource>(); | ||||||
|             ApplyFilters<TSource>(); |             ApplyFilters<TSource>(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         protected virtual void ApplyQueryExecutionOptionIncerceptors() | ||||||
|  |         { | ||||||
|  |             Options = Interceptors | ||||||
|  |                 .Where(t => t is IQueryExecutionOptionsInterceptor) | ||||||
|  |                 .Cast<IQueryExecutionOptionsInterceptor>() | ||||||
|  |                 .Aggregate(Options, (prev, curr) => curr.InterceptQueryExecutionOptions(CurrentQueryable, prev)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         public virtual void AddInterceptor(IQueryInterceptor interceptor) |         public virtual void AddInterceptor(IQueryInterceptor interceptor) | ||||||
|         { |         { | ||||||
|             if (interceptor == null) throw new ArgumentNullException("interceptor"); |             if (interceptor == null) throw new ArgumentNullException("interceptor"); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user