added feature for batch readings alterations.
This commit is contained in:
parent
9b51e2e287
commit
61642eee18
27
PoweredSoft.DynamicQuery.Core/IAfterReadInterceptor.cs
Normal file
27
PoweredSoft.DynamicQuery.Core/IAfterReadInterceptor.cs
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
public interface IQueryConvertInterceptor : IQueryInterceptor
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,8 @@ 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;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace PoweredSoft.DynamicQuery.Test
|
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
|
var shouldResult = ctx.Orders.OrderBy(t => t.Customer).GroupBy(t => t.Customer).Select(t => new
|
||||||
{
|
{
|
||||||
Customer = t.Key,
|
Customer = 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.
|
||||||
@ -35,7 +37,7 @@ namespace PoweredSoft.DynamicQuery.Test
|
|||||||
|
|
||||||
// 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(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 expected = shouldResult[0];
|
||||||
var actual = ((IGroupQueryResult)result.Data[0]);
|
var actual = ((IGroupQueryResult)result.Data[0]);
|
||||||
@ -79,5 +81,82 @@ namespace PoweredSoft.DynamicQuery.Test
|
|||||||
Assert.Equal(expected, c);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.Tasks;
|
||||||
using PoweredSoft.DynamicLinq;
|
using PoweredSoft.DynamicLinq;
|
||||||
using PoweredSoft.DynamicLinq.Fluent;
|
using PoweredSoft.DynamicLinq.Fluent;
|
||||||
using PoweredSoft.DynamicQuery.Core;
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
@ -57,13 +58,15 @@ namespace PoweredSoft.DynamicQuery
|
|||||||
var groupRecords = CurrentQueryable.ToDynamicClassList();
|
var groupRecords = CurrentQueryable.ToDynamicClassList();
|
||||||
|
|
||||||
// now join them into logical collections
|
// 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);
|
result.Aggregates = CalculateTotalAggregate<T>(queryableAfterFilters);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected virtual List<IAggregateResult> CalculateTotalAggregate<T>(IQueryable queryableAfterFilters)
|
protected virtual List<IAggregateResult> CalculateTotalAggregate<T>(IQueryable queryableAfterFilters)
|
||||||
{
|
{
|
||||||
if (!Criteria.Aggregates.Any())
|
if (!Criteria.Aggregates.Any())
|
||||||
@ -107,7 +110,7 @@ namespace PoweredSoft.DynamicQuery
|
|||||||
|
|
||||||
// data.
|
// data.
|
||||||
var entities = ((IQueryable<T>)CurrentQueryable).ToList();
|
var entities = ((IQueryable<T>)CurrentQueryable).ToList();
|
||||||
var records = InterceptConvertTo<T>(entities);
|
var records = InterceptConvertTo<T>(entities).Result;
|
||||||
result.Data = records;
|
result.Data = records;
|
||||||
|
|
||||||
// aggregates.
|
// aggregates.
|
||||||
|
@ -70,7 +70,11 @@ namespace PoweredSoft.DynamicQuery
|
|||||||
var groupRecords = await AsyncQueryableService.ToListAsync(CurrentQueryable.Cast<DynamicClass>(), cancellationToken);
|
var groupRecords = await AsyncQueryableService.ToListAsync(CurrentQueryable.Cast<DynamicClass>(), cancellationToken);
|
||||||
|
|
||||||
// now join them into logical collections
|
// now join them into logical collections
|
||||||
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);
|
result.Aggregates = await CalculateTotalAggregateAsync<T>(queryableAfterFilters, cancellationToken);
|
||||||
return result;
|
return result;
|
||||||
@ -93,7 +97,7 @@ namespace PoweredSoft.DynamicQuery
|
|||||||
|
|
||||||
// data.
|
// data.
|
||||||
var entities = await AsyncQueryableService.ToListAsync(((IQueryable<T>)CurrentQueryable), cancellationToken);
|
var entities = await AsyncQueryableService.ToListAsync(((IQueryable<T>)CurrentQueryable), cancellationToken);
|
||||||
var records = InterceptConvertTo<T>(entities);
|
var records = await InterceptConvertTo<T>(entities);
|
||||||
result.Data = records;
|
result.Data = records;
|
||||||
|
|
||||||
// aggregates.
|
// aggregates.
|
||||||
|
@ -182,15 +182,46 @@ namespace PoweredSoft.DynamicQuery
|
|||||||
return ret;
|
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();
|
var objects = entities.Cast<object>().ToList();
|
||||||
for (var i = 0; i < objects.Count; i++)
|
for (var i = 0; i < objects.Count; i++)
|
||||||
objects[i] = InterceptConvertToObject<T>(objects[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;
|
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)
|
protected virtual object InterceptConvertToObject<T>(object o)
|
||||||
{
|
{
|
||||||
o = Interceptors
|
o = Interceptors
|
||||||
@ -329,7 +360,7 @@ namespace PoweredSoft.DynamicQuery
|
|||||||
.Aggregate((IQueryable<T>)CurrentQueryable, (prev, interceptor) => interceptor.InterceptBeforeFiltering(Criteria, prev));
|
.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 groupIndex = Criteria.Groups.IndexOf(group);
|
||||||
var isLast = Criteria.Groups.Last() == group;
|
var isLast = Criteria.Groups.Last() == group;
|
||||||
@ -376,11 +407,12 @@ 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<T>>("Records")).ToList();
|
||||||
groupResult.Data = InterceptConvertTo<T>(entities);
|
groupResult.Data = entities.Cast<object>().ToList();
|
||||||
|
lastLists.Add(groupResult.Data);
|
||||||
}
|
}
|
||||||
else
|
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;
|
return groupResult;
|
||||||
@ -389,5 +421,26 @@ namespace PoweredSoft.DynamicQuery
|
|||||||
.ToList();
|
.ToList();
|
||||||
return ret;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user