added feature for batch readings alterations.

This commit is contained in:
David Lebee 2019-02-23 22:47:50 -06:00
parent 9b51e2e287
commit 61642eee18
6 changed files with 183 additions and 13 deletions

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace PoweredSoft.DynamicQuery.Core
{
public interface IAfterReadEntityInterceptor<T> : IQueryInterceptor
{
void AfterReadEntity(List<T> entities);
}
public interface IAfterReadEntityInterceptorAsync<T> : IQueryInterceptor
{
Task AfterReadEntityAsync(List<T> entities, CancellationToken cancellationToken = default(CancellationToken));
}
public interface IAfterReadInterceptor<T> : IQueryInterceptor
{
void AfterRead(List<Tuple<T, object>> pairs);
}
public interface IAfterReadInterceptorAsync<T> : IQueryInterceptor
{
Task AfterReadAsync(List<Tuple<T, object>> pairs, CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -1,4 +1,8 @@
namespace PoweredSoft.DynamicQuery.Core
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace PoweredSoft.DynamicQuery.Core
{
public interface IQueryConvertInterceptor : IQueryInterceptor
{

View File

@ -4,6 +4,8 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace PoweredSoft.DynamicQuery.Test
@ -18,7 +20,7 @@ namespace PoweredSoft.DynamicQuery.Test
var shouldResult = ctx.Orders.OrderBy(t => t.Customer).GroupBy(t => t.Customer).Select(t => new
{
Customer = t.Key,
Orders = t.ToList()
Orders = t.ToList()
}).ToList();
// query handler that is empty should be the same as running to list.
@ -35,7 +37,7 @@ namespace PoweredSoft.DynamicQuery.Test
// top level should have same amount of group levels.
Assert.Equal(result.Data.Count, shouldResult.Count);
for(var i = 0; i < shouldResult.Count; i++)
for (var i = 0; i < shouldResult.Count; i++)
{
var expected = shouldResult[0];
var actual = ((IGroupQueryResult)result.Data[0]);
@ -79,5 +81,82 @@ namespace PoweredSoft.DynamicQuery.Test
Assert.Equal(expected, c);
});
}
[Fact]
public void InterceptorsWithGrouping()
{
MockContextFactory.SeedAndTestContextFor("GroupTests_InterceptorsWithGrouping", TestSeeders.SeedTicketScenario, ctx =>
{
var criteria = new QueryCriteria()
{
Groups = new List<IGroup>()
{
new Group { Path = "TicketType" }
},
Aggregates = new List<IAggregate>()
{
new Aggregate { Type = AggregateType.Count }
}
};
var interceptor = new InterceptorsWithGrouping();
var queryHandler = new QueryHandler();
queryHandler.AddInterceptor(interceptor);
var result = queryHandler.Execute(ctx.Tickets, criteria);
Assert.Equal(4, interceptor.Count);
Assert.True(interceptor.Test);
Assert.True(interceptor.Test2);
Assert.True(interceptor.Test3);
Assert.True(interceptor.Test4);
});
}
}
class InterceptorWithGroupingFakeModel
{
}
class InterceptorsWithGrouping :
IAfterReadEntityInterceptor<Ticket>,
IAfterReadEntityInterceptorAsync<Ticket>,
IAfterReadInterceptor<Ticket>,
IAfterReadInterceptorAsync<Ticket>,
IQueryConvertInterceptor<Ticket>
{
public int Count { get; set; } = 0;
public bool Test { get; set; } = false;
public bool Test2 { get; set; } = false;
public bool Test3 { get; set; } = false;
public bool Test4 { get; set; } = false;
public void AfterRead(List<Tuple<Ticket, object>> pairs)
{
Test = true;
Count++;
}
public async Task AfterReadAsync(List<Tuple<Ticket, object>> pairs, CancellationToken cancellationToken = default(CancellationToken))
{
Test2 = true;
Count++;
}
public void AfterReadEntity(List<Ticket> entities)
{
Test3 = true;
Count++;
}
public async Task AfterReadEntityAsync(List<Ticket> entities, CancellationToken cancellationToken = default(CancellationToken))
{
Test4 = true;
Count++;
}
public object InterceptResultTo(Ticket entity)
{
return new InterceptorWithGroupingFakeModel();
}
}
}

View File

@ -4,6 +4,7 @@ using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using PoweredSoft.DynamicLinq;
using PoweredSoft.DynamicLinq.Fluent;
using PoweredSoft.DynamicQuery.Core;
@ -57,13 +58,15 @@ namespace PoweredSoft.DynamicQuery
var groupRecords = CurrentQueryable.ToDynamicClassList();
// now join them into logical collections
result.Data = RecursiveRegroup<T>(groupRecords, aggregateResults, Criteria.Groups.First());
var lastLists = new List<List<object>>();
result.Data = RecursiveRegroup<T>(groupRecords, aggregateResults, Criteria.Groups.First(), lastLists);
// intercept grouped by.
QueryInterceptToGrouped<T>(lastLists).Wait();
result.Aggregates = CalculateTotalAggregate<T>(queryableAfterFilters);
return result;
}
protected virtual List<IAggregateResult> CalculateTotalAggregate<T>(IQueryable queryableAfterFilters)
{
if (!Criteria.Aggregates.Any())
@ -107,7 +110,7 @@ namespace PoweredSoft.DynamicQuery
// data.
var entities = ((IQueryable<T>)CurrentQueryable).ToList();
var records = InterceptConvertTo<T>(entities);
var records = InterceptConvertTo<T>(entities).Result;
result.Data = records;
// aggregates.

View File

@ -70,7 +70,11 @@ namespace PoweredSoft.DynamicQuery
var groupRecords = await AsyncQueryableService.ToListAsync(CurrentQueryable.Cast<DynamicClass>(), cancellationToken);
// now join them into logical collections
result.Data = RecursiveRegroup<T>(groupRecords, aggregateResults, Criteria.Groups.First());
var lastLists = new List<List<object>>();
result.Data = RecursiveRegroup<T>(groupRecords, aggregateResults, Criteria.Groups.First(), lastLists);
// converted to grouped by.
await QueryInterceptToGrouped<T>(lastLists);
result.Aggregates = await CalculateTotalAggregateAsync<T>(queryableAfterFilters, cancellationToken);
return result;
@ -93,7 +97,7 @@ namespace PoweredSoft.DynamicQuery
// data.
var entities = await AsyncQueryableService.ToListAsync(((IQueryable<T>)CurrentQueryable), cancellationToken);
var records = InterceptConvertTo<T>(entities);
var records = await InterceptConvertTo<T>(entities);
result.Data = records;
// aggregates.

View File

@ -182,15 +182,46 @@ namespace PoweredSoft.DynamicQuery
return ret;
}
protected virtual List<object> InterceptConvertTo<T>(List<T> entities)
protected virtual async Task<List<object>> InterceptConvertTo<T>(List<T> entities)
{
await AfterEntityReadInterceptors(entities);
var objects = entities.Cast<object>().ToList();
for (var i = 0; i < objects.Count; i++)
objects[i] = InterceptConvertToObject<T>(objects[i]);
var pairs = entities.Select((t, index) => Tuple.Create(t, objects[index])).ToList();
await AfterReadInterceptors<T>(pairs);
return objects;
}
protected virtual async Task AfterEntityReadInterceptors<T>(List<T> entities)
{
Interceptors
.Where(t => t is IAfterReadEntityInterceptor<T>)
.Cast<IAfterReadEntityInterceptor<T>>()
.ToList()
.ForEach(t => t.AfterReadEntity(entities));
var asyncInterceptors = Interceptors.Where(t => t is IAfterReadEntityInterceptorAsync<T>).Cast<IAfterReadEntityInterceptorAsync<T>>();
foreach (var interceptor in asyncInterceptors)
await interceptor.AfterReadEntityAsync(entities);
}
protected virtual async Task AfterReadInterceptors<T>(List<Tuple<T, object>> pairs)
{
Interceptors
.Where(t => t is IAfterReadInterceptor<T>)
.Cast<IAfterReadInterceptor<T>>()
.ToList()
.ForEach(t => t.AfterRead(pairs));
var asyncInterceptors = Interceptors.Where(t => t is IAfterReadInterceptorAsync<T>).Cast<IAfterReadInterceptorAsync<T>>();
foreach (var interceptor in asyncInterceptors)
await interceptor.AfterReadAsync(pairs);
}
protected virtual object InterceptConvertToObject<T>(object o)
{
o = Interceptors
@ -329,7 +360,7 @@ namespace PoweredSoft.DynamicQuery
.Aggregate((IQueryable<T>)CurrentQueryable, (prev, interceptor) => interceptor.InterceptBeforeFiltering(Criteria, prev));
}
protected virtual List<object> RecursiveRegroup<T>(List<DynamicClass> groupRecords, List<List<DynamicClass>> aggregateResults, IGroup group, List<IGroupQueryResult> parentGroupResults = null)
protected virtual List<object> RecursiveRegroup<T>(List<DynamicClass> groupRecords, List<List<DynamicClass>> aggregateResults, IGroup group, List<List<object>> lastLists, List<IGroupQueryResult> parentGroupResults = null)
{
var groupIndex = Criteria.Groups.IndexOf(group);
var isLast = Criteria.Groups.Last() == group;
@ -376,11 +407,12 @@ namespace PoweredSoft.DynamicQuery
if (isLast)
{
var entities = t.SelectMany(t2 => t2.GetDynamicPropertyValue<List<T>>("Records")).ToList();
groupResult.Data = InterceptConvertTo<T>(entities);
groupResult.Data = entities.Cast<object>().ToList();
lastLists.Add(groupResult.Data);
}
else
{
groupResult.Data = RecursiveRegroup<T>(t.ToList(), aggregateResults, Criteria.Groups[groupIndex + 1], groupResults);
groupResult.Data = RecursiveRegroup<T>(t.ToList(), aggregateResults, Criteria.Groups[groupIndex + 1], lastLists, groupResults);
}
return groupResult;
@ -389,5 +421,26 @@ namespace PoweredSoft.DynamicQuery
.ToList();
return ret;
}
protected virtual async Task QueryInterceptToGrouped<T>(List<List<object>> lists)
{
var entities = lists.SelectMany(t => t).Cast<T>().ToList();
await AfterEntityReadInterceptors(entities);
var pairs = new List<Tuple<T, object>>();
lists.ForEach(innerList =>
{
for(var i = 0; i < innerList.Count; i++)
{
var entity = (T)innerList[i];
var convertedObject = InterceptConvertToObject<T>(entity);
innerList[i] = convertedObject;
pairs.Add(Tuple.Create(entity, convertedObject));
}
});
await AfterReadInterceptors<T>(pairs);
}
}
}