Advancing well.

This commit is contained in:
David Lebee 2018-10-17 21:14:21 -05:00
parent d2d28a9a45
commit 94d7b1c4d4
19 changed files with 390 additions and 139 deletions

View File

@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.DynamicQuery",
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.DynamicQuery.Cli", "PoweredSoft.DynamicQuery.Cli\PoweredSoft.DynamicQuery.Cli.csproj", "{7FC0F790-A8B9-4335-8D72-09797DEB0359}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.DynamicQuery.Cli", "PoweredSoft.DynamicQuery.Cli\PoweredSoft.DynamicQuery.Cli.csproj", "{7FC0F790-A8B9-4335-8D72-09797DEB0359}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicLinq", "..\DynamicLinq\PoweredSoft.DynamicLinq\PoweredSoft.DynamicLinq.csproj", "{E4E954E0-66FA-4D72-979A-FB2EF8356A90}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@ -27,6 +29,10 @@ Global
{7FC0F790-A8B9-4335-8D72-09797DEB0359}.Debug|Any CPU.Build.0 = Debug|Any CPU {7FC0F790-A8B9-4335-8D72-09797DEB0359}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FC0F790-A8B9-4335-8D72-09797DEB0359}.Release|Any CPU.ActiveCfg = Release|Any CPU {7FC0F790-A8B9-4335-8D72-09797DEB0359}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7FC0F790-A8B9-4335-8D72-09797DEB0359}.Release|Any CPU.Build.0 = Release|Any CPU {7FC0F790-A8B9-4335-8D72-09797DEB0359}.Release|Any CPU.Build.0 = Release|Any CPU
{E4E954E0-66FA-4D72-979A-FB2EF8356A90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E4E954E0-66FA-4D72-979A-FB2EF8356A90}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4E954E0-66FA-4D72-979A-FB2EF8356A90}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E4E954E0-66FA-4D72-979A-FB2EF8356A90}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -5,9 +5,39 @@ using System.Linq;
namespace PoweredSoft.DynamicQuery.Cli namespace PoweredSoft.DynamicQuery.Cli
{ {
public class PersonQueryInterceptor : IBeforeQueryAlteredInterceptor<Person> public class PersonQueryInterceptor : IQueryInterceptor
//, IBeforeQueryAlteredInterceptor<Person>
, IFilterInterceptor
{ {
public IQueryable<Person> InterceptQueryBeforeAltered(IQueryCriteria criteria, IQueryable<Person> queryable) => queryable.Where(t => t.FirstName == "David"); public IQueryable<Person> InterceptQueryBeforeAltered(IQueryCriteria criteria, IQueryable<Person> queryable)
=> queryable.Where(t => t.FirstName.StartsWith("Da"));
public IFilter InterceptFilter(IFilter filter)
{
if (filter is SimpleFilter)
{
var simpleFilter = filter as ISimpleFilter;
if (simpleFilter.Path == "FirstName" && simpleFilter.Value is string && ((string)simpleFilter.Value).Contains(","))
{
var firstNames = ((string) simpleFilter.Value).Split(',');
var filters = firstNames.Select(firstName => new SimpleFilter
{
Path = "FirstName",
Type = FilterType.Equal,
Value = firstName
}).Cast<IFilter>().ToList();
return new CompositeFilter
{
Type = FilterType.Composite,
Filters = filters,
And = true
};
}
}
return filter;
}
} }
public class Person public class Person
@ -30,18 +60,34 @@ namespace PoweredSoft.DynamicQuery.Cli
{ {
var list = new List<Person>() var list = new List<Person>()
{ {
new Person{ Id = 1, FirstName = "David", LastName = "Lebee "}, new Person{ Id = 1, FirstName = "David", LastName = "Lebee"},
new Person{ Id = 2, FirstName = "Michaela", LastName = "Lebee "}, new Person{ Id = 2, FirstName = "Michaela", LastName = "Lebee"},
new Person{ Id = 3, FirstName = "Zohra", LastName = "Lebee "}, new Person{ Id = 3, FirstName = "Zohra", LastName = "Lebee"},
new Person{ Id = 4, FirstName = "Eric", LastName = "Vickar "}, new Person{ Id = 4, FirstName = "Eric", LastName = "Vickar"},
new Person{ Id = 5, FirstName = "Susan", LastName = "Vickar "}, new Person{ Id = 5, FirstName = "Susan", LastName = "Vickar"},
}; };
var queryable = list.AsQueryable(); var queryable = list.AsQueryable();
var qb = new QueryBuilder(); var criteria = new QueryCriteria();
qb.AddInterceptor(new PersonQueryInterceptor());
qb.Execute(queryable, null); criteria.Filters.Add(new SimpleFilter
{
Path = "LastName",
Value = "Lebee",
Type = FilterType.Equal,
});
criteria.Filters.Add(new SimpleFilter
{
Path = "FirstName",
Value = "David,Michaela",
Type = FilterType.Equal,
});
var handler = new QueryHandler();
handler.AddInterceptor(new PersonQueryInterceptor());
handler.Execute(queryable, criteria);
} }
} }
} }

View File

@ -2,10 +2,17 @@
{ {
public enum FilterType public enum FilterType
{ {
Equals, Equal,
Contains, Contains,
StartsWith, StartsWith,
EndsWith, EndsWith,
Composite Composite,
NotEqual,
GreaterThan,
LessThanOrEqual,
GreaterThanOrEqual,
LessThan,
In,
NotIn
} }
} }

View File

@ -0,0 +1,14 @@
using System.Linq;
namespace PoweredSoft.DynamicQuery.Core
{
public interface IBeforeQueryAlteredInterceptor : IQueryInterceptor
{
IQueryable InterceptQueryBeforeAltered(IQueryCriteria criteria, IQueryable queryable);
}
public interface IBeforeQueryAlteredInterceptor<T> : IQueryInterceptor
{
IQueryable<T> InterceptQueryBeforeAltered(IQueryCriteria criteria, IQueryable<T> queryable);
}
}

View File

@ -0,0 +1,14 @@
using System.Linq;
namespace PoweredSoft.DynamicQuery.Core
{
public interface IBeforeQueryExecuteInterceptor : IQueryInterceptor
{
IQueryable InterceptBeforeQuery(IQueryCriteria criteria, IQueryable queryable);
}
public interface IBeforeQueryExecuteInterceptor<T> : IQueryInterceptor
{
IQueryable<T> InterceptBeforeQuery(IQueryCriteria criteria, IQueryable<T> queryable);
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace PoweredSoft.DynamicQuery.Core
{
public interface IFilterInterceptor : IQueryInterceptor
{
IFilter InterceptFilter(IFilter filter);
}
}

View File

@ -0,0 +1,7 @@
namespace PoweredSoft.DynamicQuery.Core
{
public interface IGroupingInterceptor : IQueryInterceptor
{
IGroup InterceptGroup(IGroup group);
}
}

View File

@ -1,55 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PoweredSoft.DynamicQuery.Core
{
public interface IQueryBuilder
{
IQueryResult Execute(IQueryable queryable, IQueryCriteria criteria);
Task<IQueryResult> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria);
void AddInterceptor(IQueryInterceptor interceptor);
}
public interface IQueryInterceptor
{
}
public interface IBeforeQueryAlteredInterceptor : IQueryInterceptor
{
IQueryable InterceptQueryBeforeAltered(IQueryCriteria criteria, IQueryable queryable);
}
public interface IBeforeQueryAlteredInterceptor<T> : IQueryInterceptor
{
IQueryable<T> InterceptQueryBeforeAltered(IQueryCriteria criteria, IQueryable<T> queryable);
}
public interface IFilterInteceptor : IQueryInterceptor
{
IEnumerable<IFilter> InterceptFilter(IFilter filter);
}
public interface IGroupingInteceptor : IQueryInterceptor
{
IGroup InterceptGroup(IGroup group);
}
public interface ISortInteceptor : IQueryInterceptor
{
IEnumerable<ISort> InterceptSort(ISort sort);
}
public interface IBeforeQueryExecuteInterceptor : IQueryInterceptor
{
IQueryable InterceptBeforeQuery(IQueryCriteria criteria, IQueryable queryable);
}
public interface IBeforeQueryExecuteInterceptor<T> : IQueryInterceptor
{
IQueryable<T> InterceptBeforeQuery(IQueryCriteria criteria, IQueryable<T> queryable);
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PoweredSoft.DynamicQuery.Core
{
public interface IQueryHandler
{
IQueryResult Execute(IQueryable queryable, IQueryCriteria criteria);
Task<IQueryResult> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria);
void AddInterceptor(IQueryInterceptor interceptor);
}
}

View File

@ -0,0 +1,7 @@
namespace PoweredSoft.DynamicQuery.Core
{
public interface IQueryInterceptor
{
}
}

View File

@ -0,0 +1,9 @@
using System.Collections.Generic;
namespace PoweredSoft.DynamicQuery.Core
{
public interface ISortInteceptor : IQueryInterceptor
{
IEnumerable<ISort> InterceptSort(ISort sort);
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Text;
using PoweredSoft.DynamicLinq;
using PoweredSoft.DynamicQuery.Core;
namespace PoweredSoft.DynamicQuery.Extensions
{
public static class FilterTypeExtensions
{
public static ConditionOperators? ConditionOperator(this FilterType filterType)
{
if (filterType == FilterType.Equal)
return ConditionOperators.Equal;
if (filterType == FilterType.NotEqual)
return ConditionOperators.NotEqual;
if (filterType == FilterType.GreaterThan)
return ConditionOperators.GreaterThan;
if (filterType == FilterType.GreaterThanOrEqual)
return ConditionOperators.GreaterThanOrEqual;
if (filterType == FilterType.LessThan)
return ConditionOperators.LessThan;
if (filterType == FilterType.LessThanOrEqual)
return ConditionOperators.LessThanOrEqual;
if (filterType == FilterType.StartsWith)
return ConditionOperators.StartsWith;
if (filterType == FilterType.EndsWith)
return ConditionOperators.EndsWith;
if (filterType == FilterType.Contains)
return ConditionOperators.Contains;
if (filterType == FilterType.In)
return ConditionOperators.In;
if (filterType == FilterType.NotIn)
return ConditionOperators.NotIn;
return null;
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
using PoweredSoft.DynamicQuery.Core;
namespace PoweredSoft.DynamicQuery
{
public abstract class Filter : IFilter
{
public bool? And { get; set; }
public FilterType Type { get; set; }
}
public class SimpleFilter : ISimpleFilter
{
public bool? And { get; set; }
public FilterType Type { get; set; }
public string Path { get; set; }
public object Value { get; set; }
}
public class CompositeFilter : ICompositeFilter
{
public bool? And { get; set; }
public FilterType Type { get; set; } = FilterType.Composite;
public List<IFilter> Filters { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using PoweredSoft.DynamicQuery.Core;
namespace PoweredSoft.DynamicQuery
{
public class Group : IGroup
{
public string Path { get; set; }
public bool? Ascending { get; set; }
}
}

View File

@ -5,6 +5,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\DynamicLinq\PoweredSoft.DynamicLinq\PoweredSoft.DynamicLinq.csproj" />
<ProjectReference Include="..\PoweredSoft.DynamicQuery.Core\PoweredSoft.DynamicQuery.Core.csproj" /> <ProjectReference Include="..\PoweredSoft.DynamicQuery.Core\PoweredSoft.DynamicQuery.Core.csproj" />
</ItemGroup> </ItemGroup>

View File

@ -1,72 +0,0 @@
using PoweredSoft.DynamicQuery.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace PoweredSoft.DynamicQuery
{
public class QueryBuilder : IQueryBuilder
{
protected List<IQueryInterceptor> Interceptors { get; } = new List<IQueryInterceptor>();
protected IQueryCriteria Criteria { get; set; }
protected IQueryable QueryableAtStart { get; private set; }
protected IQueryable CurrentQueryable { get; set; }
protected Type QueryableUnderlyingType => QueryableAtStart.ElementType;
private MethodInfo ApplyInterceptorsAndCriteriaMethod { get; } = typeof(QueryBuilder).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).First(t => t.Name == "ApplyInterceptorsAndCriteria" && t.IsGenericMethod);
protected virtual void Reset(IQueryable queryable, IQueryCriteria criteria)
{
//Criteria = criteria ?? throw new ArgumentNullException("criteria");
QueryableAtStart = queryable ?? throw new ArgumentNullException("queryable");
CurrentQueryable = QueryableAtStart;
}
public virtual void AddInterceptor(IQueryInterceptor interceptor)
{
if (interceptor == null) throw new ArgumentNullException("interceptor");
if (!Interceptors.Contains(interceptor))
Interceptors.Add(interceptor);
}
protected virtual void ApplyInterceptorsAndCriteria<T>()
{
ApplySimpleBeforeAlterInterceptors();
ApplyGenericBeforeAlterInterceptors<T>();
}
private void ApplyInterceptorsAndCriteria()
{
var genericMethod = ApplyInterceptorsAndCriteriaMethod.MakeGenericMethod(QueryableUnderlyingType);
genericMethod.Invoke(this, null);
}
protected virtual void ApplyGenericBeforeAlterInterceptors<T>()
{
var interceptors = Interceptors.Where(t => t is IBeforeQueryAlteredInterceptor<T>).Cast<IBeforeQueryAlteredInterceptor<T>>().ToList();
interceptors.ForEach(i => CurrentQueryable = i.InterceptQueryBeforeAltered(Criteria, (IQueryable<T>)CurrentQueryable));
}
protected virtual void ApplySimpleBeforeAlterInterceptors()
{
var beforeAlterInterceptors = Interceptors.Where(t => t is IBeforeQueryAlteredInterceptor).Cast<IBeforeQueryAlteredInterceptor>().ToList();
beforeAlterInterceptors.ForEach(i => CurrentQueryable = i.InterceptQueryBeforeAltered(Criteria, CurrentQueryable));
}
public virtual IQueryResult Execute(IQueryable queryable, IQueryCriteria criteria)
{
Reset(queryable, criteria);
ApplyInterceptorsAndCriteria();
return null;
}
public virtual Task<IQueryResult> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,17 @@
using PoweredSoft.DynamicQuery.Core;
using System;
using System.Collections.Generic;
using System.Text;
namespace PoweredSoft.DynamicQuery
{
public class QueryCriteria : IQueryCriteria
{
public int? Page { get; set; }
public int? PageSize { get; set; }
public List<ISort> Sorts { get; set; } = new List<ISort>();
public List<IFilter> Filters { get; set; } = new List<IFilter>();
public List<IGroup> Groups { get; set; } = new List<IGroup>();
public List<IAggregate> Aggregates { get; set; } = new List<IAggregate>();
}
}

View File

@ -0,0 +1,134 @@
using PoweredSoft.DynamicQuery.Core;
using PoweredSoft.DynamicLinq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using PoweredSoft.DynamicLinq.Fluent;
using PoweredSoft.DynamicQuery.Extensions;
namespace PoweredSoft.DynamicQuery
{
public class QueryHandler : IQueryHandler
{
protected List<IQueryInterceptor> Interceptors { get; } = new List<IQueryInterceptor>();
protected IQueryCriteria Criteria { get; set; }
protected IQueryable QueryableAtStart { get; private set; }
protected IQueryable CurrentQueryable { get; set; }
protected Type QueryableUnderlyingType => QueryableAtStart.ElementType;
private MethodInfo ApplyInterceptorsAndCriteriaMethod { get; } = typeof(QueryHandler).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).First(t => t.Name == "ApplyInterceptorsAndCriteria" && t.IsGenericMethod);
protected virtual void Reset(IQueryable queryable, IQueryCriteria criteria)
{
Criteria = criteria ?? throw new ArgumentNullException("criteria");
QueryableAtStart = queryable ?? throw new ArgumentNullException("queryable");
CurrentQueryable = QueryableAtStart;
}
public virtual void AddInterceptor(IQueryInterceptor interceptor)
{
if (interceptor == null) throw new ArgumentNullException("interceptor");
if (!Interceptors.Contains(interceptor))
Interceptors.Add(interceptor);
}
protected virtual void ApplyInterceptorsAndCriteria<T>()
{
ApplySimpleBeforeAlterInterceptors();
ApplyGenericBeforeAlterInterceptors<T>();
ApplyFilters<T>();
}
protected virtual ConditionOperators? ResolveFromOrDefault(FilterType filterType) =>
filterType.ConditionOperator();
protected virtual ConditionOperators ResolveFrom(FilterType filterType)
{
var ret = ResolveFromOrDefault(filterType);
if (ret == null)
throw new NotSupportedException($"{filterType} is not supported");
return ret.Value;
}
protected virtual void ApplyFilters<T>()
{
CurrentQueryable = CurrentQueryable.Query(whereBuilder =>
{
Criteria.Filters.ForEach(filter => ApplyFilter(whereBuilder, filter));
});
}
protected virtual void ApplyFilter(WhereBuilder whereBuilder, IFilter filter)
{
var transformedFilter = InterceptFilter(filter);
if (transformedFilter is ISimpleFilter)
ApplySimpleFilter(whereBuilder, transformedFilter as ISimpleFilter);
else if (transformedFilter is ICompositeFilter)
AppleCompositeFilter(whereBuilder, transformedFilter as ICompositeFilter);
else
throw new NotSupportedException();
}
protected virtual void AppleCompositeFilter(WhereBuilder whereBuilder, ICompositeFilter filter)
{
whereBuilder.SubQuery(subWhereBuilder => filter.Filters.ForEach(subFilter => ApplyFilter(subWhereBuilder, subFilter)), filter.And == true);
}
protected virtual void ApplySimpleFilter(WhereBuilder whereBuilder, ISimpleFilter filter)
{
var resolvedConditionOperator = ResolveFrom(filter.Type);
whereBuilder.Compare(filter.Path, resolvedConditionOperator, filter.Value, and: filter.And == true);
}
private IFilter InterceptFilter(IFilter filter)
{
var ret = Interceptors.Where(t => t is IFilterInterceptor)
.Cast<IFilterInterceptor>()
.Aggregate(filter, (previousFilter, interceptor) => interceptor.InterceptFilter(previousFilter));
return ret;
}
private void ApplyInterceptorsAndCriteria()
{
var genericMethod = ApplyInterceptorsAndCriteriaMethod.MakeGenericMethod(QueryableUnderlyingType);
genericMethod.Invoke(this, null);
}
protected virtual void ApplyGenericBeforeAlterInterceptors<T>()
{
CurrentQueryable = Interceptors
.Where(t => t is IBeforeQueryAlteredInterceptor<T>)
.Cast<IBeforeQueryAlteredInterceptor<T>>()
.Aggregate((IQueryable<T>)CurrentQueryable, (prev, interceptor) => interceptor.InterceptQueryBeforeAltered(Criteria, prev));
}
protected virtual void ApplySimpleBeforeAlterInterceptors()
{
CurrentQueryable = Interceptors
.Where(t => t is IBeforeQueryAlteredInterceptor)
.Cast<IBeforeQueryAlteredInterceptor>()
.Aggregate(CurrentQueryable, (prev, interceptor) => interceptor.InterceptQueryBeforeAltered(Criteria, prev));
}
public virtual IQueryResult Execute(IQueryable queryable, IQueryCriteria criteria)
{
Reset(queryable, criteria);
ApplyInterceptorsAndCriteria();
var debug = CurrentQueryable.ToObjectList();
return null;
}
public virtual Task<IQueryResult> ExecuteAsync(IQueryable queryable, IQueryCriteria criteria)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using PoweredSoft.DynamicQuery.Core;
namespace PoweredSoft.DynamicQuery
{
public class Sort : ISort
{
public string Path { get; set; }
public bool? Ascending { get; set; }
}
}