strongly typed returned interfaces, next is the async mode.
This commit is contained in:
parent
61642eee18
commit
9e6dc08e53
@ -13,11 +13,14 @@ 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<object>> 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; set; }
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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,141 +1,141 @@
|
|||||||
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.Test.Mock;
|
//using PoweredSoft.DynamicQuery.Test.Mock;
|
||||||
using System;
|
//using System;
|
||||||
using System.Collections.Generic;
|
//using System.Collections.Generic;
|
||||||
using System.Linq;
|
//using System.Linq;
|
||||||
using System.Text;
|
//using System.Text;
|
||||||
using System.Threading.Tasks;
|
//using System.Threading.Tasks;
|
||||||
using Xunit;
|
//using Xunit;
|
||||||
|
|
||||||
namespace PoweredSoft.DynamicQuery.Test
|
//namespace PoweredSoft.DynamicQuery.Test
|
||||||
{
|
//{
|
||||||
public class AsyncTests
|
// public class AsyncTests
|
||||||
{
|
// {
|
||||||
[Fact]
|
// [Fact]
|
||||||
public void TestEmptyCriteria()
|
// public void TestEmptyCriteria()
|
||||||
{
|
// {
|
||||||
MockContextFactory.SeedAndTestContextFor("AsyncTests_TestEmptyCriteria", TestSeeders.SimpleSeedScenario, async ctx =>
|
// MockContextFactory.SeedAndTestContextFor("AsyncTests_TestEmptyCriteria", TestSeeders.SimpleSeedScenario, async ctx =>
|
||||||
{
|
// {
|
||||||
var resultShouldMatch = ctx.Items.ToList();
|
// var resultShouldMatch = ctx.Items.ToList();
|
||||||
var queryable = ctx.Items.AsQueryable();
|
// var queryable = ctx.Items.AsQueryable();
|
||||||
|
|
||||||
// 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 aqf = new AsyncQueryableService(new[] { new AsyncQueryableHandlerService() });
|
// var aqf = new AsyncQueryableService(new[] { new AsyncQueryableHandlerService() });
|
||||||
var criteria = new QueryCriteria();
|
// var criteria = new QueryCriteria();
|
||||||
var queryHandler = new QueryHandlerAsync(aqf);
|
// var queryHandler = new QueryHandlerAsync(aqf);
|
||||||
var result = await queryHandler.ExecuteAsync(queryable, criteria);
|
// var result = await queryHandler.ExecuteAsync(queryable, criteria);
|
||||||
Assert.Equal(resultShouldMatch, result.Data);
|
// Assert.Equal(resultShouldMatch, result.Data);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
[Fact]
|
// [Fact]
|
||||||
public void WithGrouping()
|
// public void WithGrouping()
|
||||||
{
|
// {
|
||||||
MockContextFactory.SeedAndTestContextFor("AsyncTests_WithGrouping", TestSeeders.SimpleSeedScenario, async ctx =>
|
// MockContextFactory.SeedAndTestContextFor("AsyncTests_WithGrouping", TestSeeders.SimpleSeedScenario, async ctx =>
|
||||||
{
|
// {
|
||||||
var shouldResults = ctx.OrderItems
|
// var shouldResults = ctx.OrderItems
|
||||||
.GroupBy(t => t.Order.CustomerId)
|
// .GroupBy(t => t.Order.CustomerId)
|
||||||
.Select(t => new
|
// .Select(t => new
|
||||||
{
|
// {
|
||||||
GroupValue = t.Key,
|
// GroupValue = t.Key,
|
||||||
Count = t.Count(),
|
// Count = t.Count(),
|
||||||
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)
|
||||||
})
|
// })
|
||||||
.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 = "Order.CustomerId" }
|
// new Group { Path = "Order.CustomerId" }
|
||||||
},
|
// },
|
||||||
Aggregates = new List<Core.IAggregate>
|
// Aggregates = new List<Core.IAggregate>
|
||||||
{
|
// {
|
||||||
new Aggregate { Type = AggregateType.Count },
|
// new Aggregate { Type = AggregateType.Count },
|
||||||
new Aggregate { Type = AggregateType.Avg, Path = "Quantity" },
|
// new Aggregate { Type = AggregateType.Avg, Path = "Quantity" },
|
||||||
new Aggregate { Type = AggregateType.Sum, Path = "Quantity" },
|
// new Aggregate { Type = AggregateType.Sum, Path = "Quantity" },
|
||||||
new Aggregate { Type = AggregateType.Avg, Path = "PriceAtTheTime"}
|
// new Aggregate { Type = AggregateType.Avg, Path = "PriceAtTheTime"}
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
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.Data.Cast<IGroupQueryResult>().ToList();
|
||||||
|
|
||||||
// validate group and aggregates of groups.
|
// // validate group and aggregates of groups.
|
||||||
Assert.Equal(groups.Count, shouldResults.Count);
|
// Assert.Equal(groups.Count, shouldResults.Count);
|
||||||
Assert.All(groups, g =>
|
// Assert.All(groups, g =>
|
||||||
{
|
// {
|
||||||
var index = groups.IndexOf(g);
|
// var index = groups.IndexOf(g);
|
||||||
var shouldResult = shouldResults[index];
|
// var shouldResult = shouldResults[index];
|
||||||
|
|
||||||
// validate the group value.
|
// // validate the group value.
|
||||||
Assert.Equal(g.GroupValue, shouldResult.GroupValue);
|
// Assert.Equal(g.GroupValue, shouldResult.GroupValue);
|
||||||
|
|
||||||
// validate the group aggregates.
|
// // validate the group aggregates.
|
||||||
var aggCount = g.Aggregates.First(t => t.Type == AggregateType.Count);
|
// var aggCount = g.Aggregates.First(t => t.Type == AggregateType.Count);
|
||||||
var aggItemQuantityAverage = g.Aggregates.First(t => t.Type == AggregateType.Avg && t.Path == "Quantity");
|
// 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 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");
|
// var aggAvgOfPrice = g.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.ItemQuantityAverage, aggItemQuantityAverage.Value);
|
// Assert.Equal(shouldResult.ItemQuantityAverage, aggItemQuantityAverage.Value);
|
||||||
Assert.Equal(shouldResult.ItemQuantitySum, aggItemQuantitySum.Value);
|
// Assert.Equal(shouldResult.ItemQuantitySum, aggItemQuantitySum.Value);
|
||||||
Assert.Equal(shouldResult.AvgOfPrice, aggAvgOfPrice.Value);
|
// Assert.Equal(shouldResult.AvgOfPrice, aggAvgOfPrice.Value);
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
[Fact]
|
// [Fact]
|
||||||
public void SimpleFilter()
|
// public void SimpleFilter()
|
||||||
{
|
// {
|
||||||
MockContextFactory.SeedAndTestContextFor("AsyncTests_SimpleFilter", TestSeeders.SimpleSeedScenario, async ctx =>
|
// MockContextFactory.SeedAndTestContextFor("AsyncTests_SimpleFilter", TestSeeders.SimpleSeedScenario, async ctx =>
|
||||||
{
|
// {
|
||||||
var resultShouldMatch = ctx.Items.Where(t => t.Name.EndsWith("Cables")).ToList();
|
// var resultShouldMatch = ctx.Items.Where(t => t.Name.EndsWith("Cables")).ToList();
|
||||||
|
|
||||||
var criteria = new QueryCriteria()
|
// var criteria = new QueryCriteria()
|
||||||
{
|
// {
|
||||||
Filters = new List<IFilter>
|
// Filters = new List<IFilter>
|
||||||
{
|
// {
|
||||||
new SimpleFilter
|
// new SimpleFilter
|
||||||
{
|
// {
|
||||||
Path = "Name",
|
// Path = "Name",
|
||||||
Type = FilterType.EndsWith,
|
// Type = FilterType.EndsWith,
|
||||||
Value = "Cables"
|
// Value = "Cables"
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
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.Items, criteria);
|
// var result = await queryHandler.ExecuteAsync(ctx.Items, criteria);
|
||||||
Assert.Equal(resultShouldMatch, result.Data);
|
// Assert.Equal(resultShouldMatch, result.Data);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
[Fact]
|
// [Fact]
|
||||||
public void TestPaging()
|
// public void TestPaging()
|
||||||
{
|
// {
|
||||||
MockContextFactory.SeedAndTestContextFor("AsyncTests_TestPagging", TestSeeders.SimpleSeedScenario, async ctx =>
|
// MockContextFactory.SeedAndTestContextFor("AsyncTests_TestPagging", TestSeeders.SimpleSeedScenario, async ctx =>
|
||||||
{
|
// {
|
||||||
var resultShouldMatch = ctx.OrderItems.OrderBy(t => t.Id).Skip(5).Take(5).ToList();
|
// 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.
|
// // query handler that is empty should be the same as running to list.
|
||||||
var criteria = new QueryCriteria();
|
// var criteria = new QueryCriteria();
|
||||||
criteria.Sorts.Add(new Sort("Id"));
|
// criteria.Sorts.Add(new Sort("Id"));
|
||||||
criteria.Page = 2;
|
// criteria.Page = 2;
|
||||||
criteria.PageSize = 5;
|
// criteria.PageSize = 5;
|
||||||
|
|
||||||
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);
|
||||||
Assert.Equal(resultShouldMatch, result.Data);
|
// Assert.Equal(resultShouldMatch, result.Data);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
//}
|
||||||
|
@ -63,7 +63,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 +76,7 @@ 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));
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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 QueryGroupExecutionResult<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 IQueryExecutionResult<TSource> Execute<TSource>(IQueryable<TSource> queryable, IQueryCriteria criteria)
|
||||||
public virtual IQueryExecutionResult Execute(IQueryable 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>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ 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);
|
internal MethodInfo ExecuteAsyncGeneric = typeof(QueryHandlerAsync).GetMethods(BindingFlags.Instance | BindingFlags.NonPublic).First(t => t.Name == "ExecuteAsync" && t.IsGenericMethod);
|
||||||
@ -134,5 +135,5 @@ namespace PoweredSoft.DynamicQuery
|
|||||||
var finalResult = whenAllResult.ToList();
|
var finalResult = whenAllResult.ToList();
|
||||||
return finalResult;
|
return finalResult;
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
@ -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,47 @@ 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, (object)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, object>> pairs)
|
||||||
{
|
{
|
||||||
Interceptors
|
Interceptors
|
||||||
.Where(t => t is IAfterReadInterceptor<T>)
|
.Where(t => t is IAfterReadInterceptor<TSource>)
|
||||||
.Cast<IAfterReadInterceptor<T>>()
|
.Cast<IAfterReadInterceptor<TSource>>()
|
||||||
.ToList()
|
.ToList()
|
||||||
.ForEach(t => t.AfterRead(pairs));
|
.ForEach(t => t.AfterRead(pairs));
|
||||||
|
|
||||||
var asyncInterceptors = Interceptors.Where(t => t is IAfterReadInterceptorAsync<T>).Cast<IAfterReadInterceptorAsync<T>>();
|
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(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 +230,20 @@ 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;
|
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 +259,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 +292,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 +334,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 +342,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 +355,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 +371,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 +406,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, object>>();
|
||||||
|
|
||||||
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 as object));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await AfterReadInterceptors<T>(pairs);
|
await AfterReadInterceptors<TSource, TRecord>(pairs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,25 +19,32 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 QueryGroupExecutionResult<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 { get; set; }
|
||||||
|
public List<IGroupQueryResult<TRecord>> SubGroups { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ namespace PoweredSoft.DynamicQuery
|
|||||||
services.TryAddTransient<IGroup, Group>();
|
services.TryAddTransient<IGroup, Group>();
|
||||||
services.TryAddTransient<IQueryCriteria, QueryCriteria>();
|
services.TryAddTransient<IQueryCriteria, QueryCriteria>();
|
||||||
services.TryAddTransient<IQueryHandler, QueryHandler>();
|
services.TryAddTransient<IQueryHandler, QueryHandler>();
|
||||||
services.TryAddTransient<IQueryHandlerAsync, QueryHandlerAsync>();
|
//services.TryAddTransient<IQueryHandlerAsync, QueryHandlerAsync>();
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user