From ce07c3f6d2be147effea8412d0228060f0fd47d4 Mon Sep 17 00:00:00 2001 From: David Lebee Date: Thu, 6 Dec 2018 23:08:16 -0600 Subject: [PATCH] Async seems to work very well :) --- PoweredSoft.DynamicQuery.Test/AsyncTests.cs | 138 ++++++++++++++++++ .../Mock/MockContextFactory.cs | 2 + .../PoweredSoft.DynamicQuery.Test.csproj | 2 + PoweredSoft.DynamicQuery/QueryHandlerAsync.cs | 6 +- 4 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 PoweredSoft.DynamicQuery.Test/AsyncTests.cs diff --git a/PoweredSoft.DynamicQuery.Test/AsyncTests.cs b/PoweredSoft.DynamicQuery.Test/AsyncTests.cs new file mode 100644 index 0000000..24ddcd0 --- /dev/null +++ b/PoweredSoft.DynamicQuery.Test/AsyncTests.cs @@ -0,0 +1,138 @@ +using PoweredSoft.Data.EntityFrameworkCore; +using PoweredSoft.DynamicQuery.Core; +using PoweredSoft.DynamicQuery.Test.Mock; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace PoweredSoft.DynamicQuery.Test +{ + public class AsyncTests + { + [Fact] + public void TestEmptyCriteria() + { + MockContextFactory.SeedAndTestContextFor("AsyncTests_TestEmptyCriteria", TestSeeders.SimpleSeedScenario, async ctx => + { + var resultShouldMatch = ctx.Items.ToList(); + var queryable = ctx.Items.AsQueryable(); + + // query handler that is empty should be the same as running to list. + var aqf = new AsyncQueryableFactory(); + var criteria = new QueryCriteria(); + var queryHandler = new QueryHandlerAsync(aqf); + var result = await queryHandler.ExecuteAsync(queryable, criteria); + Assert.Equal(resultShouldMatch, result.Data); + }); + } + + [Fact] + public void WithGrouping() + { + MockContextFactory.SeedAndTestContextFor("AsyncTests_WithGrouping", 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 + { + new Group { Path = "Order.CustomerId" } + }, + Aggregates = new List + { + 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 queryHandler = new QueryHandlerAsync(new AsyncQueryableFactory()); + var result = await queryHandler.ExecuteAsync(ctx.OrderItems, criteria); + var groups = result.Data.Cast().ToList(); + + // 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); + }); + }); + } + + [Fact] + public void SimpleFilter() + { + MockContextFactory.SeedAndTestContextFor("AsyncTests_SimpleFilter", TestSeeders.SimpleSeedScenario, async ctx => + { + var resultShouldMatch = ctx.Items.Where(t => t.Name.EndsWith("Cables")).ToList(); + + var criteria = new QueryCriteria() + { + Filters = new List + { + new SimpleFilter + { + Path = "Name", + Type = FilterType.EndsWith, + Value = "Cables" + } + } + }; + + var queryHandler = new QueryHandlerAsync(new AsyncQueryableFactory()); + var result = await queryHandler.ExecuteAsync(ctx.Items, criteria); + Assert.Equal(resultShouldMatch, result.Data); + }); + } + + [Fact] + public void TestPaging() + { + MockContextFactory.SeedAndTestContextFor("AsyncTests_TestPagging", TestSeeders.SimpleSeedScenario, async ctx => + { + var resultShouldMatch = ctx.OrderItems.OrderBy(t => t.Id).Skip(5).Take(5).ToList(); + + // query handler that is empty should be the same as running to list. + var criteria = new QueryCriteria(); + criteria.Sorts.Add(new Sort("Id")); + criteria.Page = 2; + criteria.PageSize = 5; + + var queryHandler = new QueryHandlerAsync(new AsyncQueryableFactory()); + var result = await queryHandler.ExecuteAsync(ctx.OrderItems, criteria); + Assert.Equal(resultShouldMatch, result.Data); + }); + } + } + +} diff --git a/PoweredSoft.DynamicQuery.Test/Mock/MockContextFactory.cs b/PoweredSoft.DynamicQuery.Test/Mock/MockContextFactory.cs index a760bec..16765c9 100644 --- a/PoweredSoft.DynamicQuery.Test/Mock/MockContextFactory.cs +++ b/PoweredSoft.DynamicQuery.Test/Mock/MockContextFactory.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Engine.ClientProtocol; using Xunit.Sdk; @@ -21,5 +22,6 @@ namespace PoweredSoft.DynamicQuery.Test.Mock seedAction(testName); TestContextFor(testName, action); } + } } diff --git a/PoweredSoft.DynamicQuery.Test/PoweredSoft.DynamicQuery.Test.csproj b/PoweredSoft.DynamicQuery.Test/PoweredSoft.DynamicQuery.Test.csproj index 96b2f4d..eab8019 100644 --- a/PoweredSoft.DynamicQuery.Test/PoweredSoft.DynamicQuery.Test.csproj +++ b/PoweredSoft.DynamicQuery.Test/PoweredSoft.DynamicQuery.Test.csproj @@ -16,6 +16,8 @@ + + diff --git a/PoweredSoft.DynamicQuery/QueryHandlerAsync.cs b/PoweredSoft.DynamicQuery/QueryHandlerAsync.cs index e6cda99..9915e7c 100644 --- a/PoweredSoft.DynamicQuery/QueryHandlerAsync.cs +++ b/PoweredSoft.DynamicQuery/QueryHandlerAsync.cs @@ -12,7 +12,7 @@ namespace PoweredSoft.DynamicQuery public class QueryHandlerAsync : QueryHandlerBase, IQueryHandlerAsync { public IAsyncQueryableFactory AsyncQueryableFactory { get; } - internal MethodInfo ExecuteAsyncGeneric = typeof(QueryHandler).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).First(t => t.Name == "ExecuteAsync" && t.IsGenericMethod); + internal MethodInfo ExecuteAsyncGeneric = typeof(QueryHandlerAsync).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).First(t => t.Name == "ExecuteAsync" && t.IsGenericMethod); internal Task ExecuteAsyncReflected(CancellationToken cancellationToken) => (Task)ExecuteAsyncGeneric.MakeGenericMethod(QueryableUnderlyingType).Invoke(this, new object[] { cancellationToken }); public QueryHandlerAsync(IAsyncQueryableFactory asyncQueryableFactory) @@ -20,7 +20,7 @@ namespace PoweredSoft.DynamicQuery AsyncQueryableFactory = asyncQueryableFactory; } - public Task ExecuteAsync(IQueryable queryable, IQueryCriteria criteria, CancellationToken cancellationToken = default(CancellationToken)) + protected virtual Task ExecuteAsync(CancellationToken cancellationToken = default(CancellationToken)) { CommonBeforeExecute(); return HasGrouping ? ExecuteAsyncGrouping(cancellationToken) : ExecuteAsyncNoGrouping(cancellationToken); @@ -120,7 +120,7 @@ namespace PoweredSoft.DynamicQuery { IQueryable selectExpression = CreateFetchAggregateSelectExpression(fg, previousGroups); var selectExpressionCasted = selectExpression.Cast(); - var aggregateResult = AsyncQueryableFactory.ToListAsync(selectExpressionCasted, cancellationToken); + var aggregateResult = AsyncQueryableFactory.ToListAsync(selectExpressionCasted, cancellationToken); previousGroups.Add(fg); return aggregateResult; }));