added some extension and made available the code to bind easily to aspnetcore trough interface.
This commit is contained in:
parent
89f4abb159
commit
a1a5a57383
@ -17,7 +17,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
README.md = README.md
|
README.md = README.md
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.DynamicQuery.Test", "PoweredSoft.DynamicQuery.Test\PoweredSoft.DynamicQuery.Test.csproj", "{3EAD8217-8E10-4261-9055-50444905922C}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PoweredSoft.DynamicQuery.Test", "PoweredSoft.DynamicQuery.Test\PoweredSoft.DynamicQuery.Test.csproj", "{3EAD8217-8E10-4261-9055-50444905922C}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PoweredSoft.DynamicQuery.AspNetCore", "PoweredSoft.DynamicQuery.AspNetCore\PoweredSoft.DynamicQuery.AspNetCore.csproj", "{DF58BD18-AB47-4018-B1EA-D1118D93B408}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@ -45,6 +47,10 @@ Global
|
|||||||
{3EAD8217-8E10-4261-9055-50444905922C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{3EAD8217-8E10-4261-9055-50444905922C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{3EAD8217-8E10-4261-9055-50444905922C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{3EAD8217-8E10-4261-9055-50444905922C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{3EAD8217-8E10-4261-9055-50444905922C}.Release|Any CPU.Build.0 = Release|Any CPU
|
{3EAD8217-8E10-4261-9055-50444905922C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DF58BD18-AB47-4018-B1EA-D1118D93B408}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DF58BD18-AB47-4018-B1EA-D1118D93B408}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DF58BD18-AB47-4018-B1EA-D1118D93B408}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DF58BD18-AB47-4018-B1EA-D1118D93B408}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace PoweredSoft.DynamicQuery.AspNetCore.Json
|
||||||
|
{
|
||||||
|
public class DynamicQueryJsonConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public override bool CanRead => true;
|
||||||
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
|
private Type[] DynamicQueryTypes { get; } = new Type[]
|
||||||
|
{
|
||||||
|
typeof(IFilter),
|
||||||
|
typeof(ISimpleFilter),
|
||||||
|
typeof(ICompositeFilter),
|
||||||
|
typeof(IAggregate),
|
||||||
|
typeof(ISort),
|
||||||
|
typeof(IGroup),
|
||||||
|
typeof(IQueryCriteria),
|
||||||
|
typeof(IQueryHandler)
|
||||||
|
};
|
||||||
|
|
||||||
|
public IServiceProvider ServiceProvider { get; }
|
||||||
|
|
||||||
|
public DynamicQueryJsonConverter(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
ServiceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType) => objectType.IsInterface && DynamicQueryTypes.Contains(objectType);
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.Null)
|
||||||
|
return (object)null;
|
||||||
|
|
||||||
|
if (objectType == typeof(IFilter))
|
||||||
|
{
|
||||||
|
var jo = JObject.Load(reader);
|
||||||
|
|
||||||
|
bool isComposite = false;
|
||||||
|
if (jo.ContainsKey("type"))
|
||||||
|
{
|
||||||
|
isComposite = jo.GetValue("type").Value<string>()
|
||||||
|
.Equals("composite", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
else if (jo.ContainsKey("Type"))
|
||||||
|
{
|
||||||
|
isComposite = jo.GetValue("Type").Value<string>()
|
||||||
|
.Equals("composite", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("IFilter should have a type property..");
|
||||||
|
}
|
||||||
|
|
||||||
|
var filterObj = ServiceProvider.GetService(isComposite ? typeof(ICompositeFilter) : typeof(ISimpleFilter));
|
||||||
|
filterObj = jo.ToObject(filterObj.GetType());
|
||||||
|
return filterObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj = ServiceProvider.GetService(objectType);
|
||||||
|
if (obj == null)
|
||||||
|
throw new JsonSerializationException("No object created.");
|
||||||
|
|
||||||
|
serializer.Populate(reader, obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
PoweredSoft.DynamicQuery.AspNetCore/MvcBuilderExtensions.cs
Normal file
20
PoweredSoft.DynamicQuery.AspNetCore/MvcBuilderExtensions.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using PoweredSoft.DynamicQuery.AspNetCore.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace PoweredSoft.DynamicQuery.AspNetCore
|
||||||
|
{
|
||||||
|
public static class MvcBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IMvcBuilder AddDynamicQueryJsonConverter(this IMvcBuilder builder, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
builder.AddJsonOptions(o =>
|
||||||
|
{
|
||||||
|
o.SerializerSettings.Converters.Add(new DynamicQueryJsonConverter(serviceProvider));
|
||||||
|
});
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
<Copyright>Powered Softwares Inc.</Copyright>
|
||||||
|
<PackageLicenseUrl>MIT</PackageLicenseUrl>
|
||||||
|
<PackageProjectUrl>https://github.com/PoweredSoft/DynamicQuery</PackageProjectUrl>
|
||||||
|
<RepositoryUrl>https://github.com/PoweredSoft/DynamicQuery</RepositoryUrl>
|
||||||
|
<RepositoryType>github</RepositoryType>
|
||||||
|
<PackageTags>powered,soft,dynamic,criteria,query,builder,asp,net,core</PackageTags>
|
||||||
|
<Version>1.0.0</Version>
|
||||||
|
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;r=g&amp;d=retro</PackageIconUrl>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Formatters.Json" Version="2.1.3" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\PoweredSoft.DynamicQuery.Core\PoweredSoft.DynamicQuery.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\PoweredSoft.DynamicQuery\PoweredSoft.DynamicQuery.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
@ -0,0 +1,22 @@
|
|||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace PoweredSoft.DynamicQuery.AspNetCore
|
||||||
|
{
|
||||||
|
public static class ServiceCollectionExtensions
|
||||||
|
{
|
||||||
|
public static void AddDynamicQueryDefaultMappings(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddTransient<ISort, Sort>();
|
||||||
|
services.AddTransient<IAggregate, Aggregate>();
|
||||||
|
services.AddTransient<ISimpleFilter, SimpleFilter>();
|
||||||
|
services.AddTransient<ICompositeFilter, CompositeFilter>();
|
||||||
|
services.AddTransient<IGroup, Group>();
|
||||||
|
services.AddTransient<IQueryCriteria, QueryCriteria>();
|
||||||
|
services.AddTransient<IQueryHandler, QueryHandler>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
namespace PoweredSoft.DynamicQuery.Core
|
using System;
|
||||||
|
|
||||||
|
namespace PoweredSoft.DynamicQuery.Core
|
||||||
{
|
{
|
||||||
public interface IFilter
|
public interface IFilter
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
@ -21,6 +22,19 @@ namespace PoweredSoft.DynamicQuery.Test
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class MockFilterInterceptorAWithExtension : IFilterInterceptor
|
||||||
|
{
|
||||||
|
public IFilter InterceptFilter(IFilter filter)
|
||||||
|
{
|
||||||
|
if (filter.IsSimpleFilterOn("CustomerFirstName"))
|
||||||
|
return filter.ReplaceByOn<Order>(t => t.Customer.FirstName);
|
||||||
|
else if (filter.IsSimpleFilterOn("CustomerFullName"))
|
||||||
|
return filter.ReplaceByCompositeOn<Order>(t => t.Customer.FirstName, t => t.Customer.LastName);
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class MockFilterInterceptorB : IFilterInterceptor
|
private class MockFilterInterceptorB : IFilterInterceptor
|
||||||
{
|
{
|
||||||
public IFilter InterceptFilter(IFilter filter)
|
public IFilter InterceptFilter(IFilter filter)
|
||||||
@ -54,6 +68,56 @@ namespace PoweredSoft.DynamicQuery.Test
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SimpleWithExtensions()
|
||||||
|
{
|
||||||
|
MockContextFactory.SeedAndTestContextFor("FilterInterceptorTests_SimpleWithExtensions", TestSeeders.SimpleSeedScenario, ctx =>
|
||||||
|
{
|
||||||
|
var queryable = ctx.Orders.AsQueryable();
|
||||||
|
|
||||||
|
var criteria = new QueryCriteria()
|
||||||
|
{
|
||||||
|
Filters = new List<IFilter>
|
||||||
|
{
|
||||||
|
new SimpleFilter { Path = "CustomerFirstName", Value = "David", Type = FilterType.Contains }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var query = new QueryHandler();
|
||||||
|
query.AddInterceptor(new MockFilterInterceptorAWithExtension());
|
||||||
|
var result = query.Execute(queryable, criteria);
|
||||||
|
|
||||||
|
var actual = result.Data;
|
||||||
|
var expected = queryable.Where(t => t.Customer.FirstName == "David").ToList();
|
||||||
|
Assert.Equal(expected, actual);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SimpleWithExtensions2()
|
||||||
|
{
|
||||||
|
MockContextFactory.SeedAndTestContextFor("FilterInterceptorTests_SimpleWithExtensions2", TestSeeders.SimpleSeedScenario, ctx =>
|
||||||
|
{
|
||||||
|
var queryable = ctx.Orders.AsQueryable();
|
||||||
|
|
||||||
|
var criteria = new QueryCriteria()
|
||||||
|
{
|
||||||
|
Filters = new List<IFilter>
|
||||||
|
{
|
||||||
|
new SimpleFilter { Path = "CustomerFullName", Value = "Da", Type = FilterType.Contains }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var query = new QueryHandler();
|
||||||
|
query.AddInterceptor(new MockFilterInterceptorAWithExtension());
|
||||||
|
var result = query.Execute(queryable, criteria);
|
||||||
|
|
||||||
|
var actual = result.Data;
|
||||||
|
var expected = queryable.Where(t => t.Customer.FirstName.Contains("Da") || t.Customer.LastName.Contains("Da")).ToList();
|
||||||
|
Assert.Equal(expected, actual);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Multi()
|
public void Multi()
|
||||||
{
|
{
|
||||||
|
97
PoweredSoft.DynamicQuery/Extensions/FilterExtensions.cs
Normal file
97
PoweredSoft.DynamicQuery/Extensions/FilterExtensions.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
using PoweredSoft.DynamicQuery.Core;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace PoweredSoft.DynamicQuery.Extensions
|
||||||
|
{
|
||||||
|
public static class FilterExtensions
|
||||||
|
{
|
||||||
|
public static bool IsSimpleFilter(this IFilter filter) => filter is ISimpleFilter;
|
||||||
|
public static bool IsCompositeFilter(this IFilter filter) => filter is ICompositeFilter;
|
||||||
|
|
||||||
|
public static bool IsSimpleFilterOn(this IFilter filter, string path)
|
||||||
|
{
|
||||||
|
var simpleFilter = filter as ISimpleFilter;
|
||||||
|
if (simpleFilter == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var result = simpleFilter.Path?.Equals(path, StringComparison.InvariantCultureIgnoreCase) == true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsSimpleFilterOn<T>(this IFilter filter, Expression<Func<T, object>> expr)
|
||||||
|
{
|
||||||
|
var resolved = GetPropertySymbol(expr);
|
||||||
|
return filter.IsSimpleFilterOn(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ISimpleFilter ReplaceByOn(this IFilter filter, string path)
|
||||||
|
{
|
||||||
|
var simpleFilter = filter as ISimpleFilter;
|
||||||
|
if (simpleFilter == null)
|
||||||
|
throw new Exception("Must be a simple filter");
|
||||||
|
|
||||||
|
var ret = new SimpleFilter();
|
||||||
|
ret.And = filter.And;
|
||||||
|
ret.Type = filter.Type;
|
||||||
|
ret.Value = simpleFilter.Value;
|
||||||
|
ret.Path = path;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ISimpleFilter ReplaceByOn<T>(this IFilter filter, Expression<Func<T, object>> expr)
|
||||||
|
{
|
||||||
|
var resolved = GetPropertySymbol(expr);
|
||||||
|
return filter.ReplaceByOn(resolved);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ICompositeFilter ReplaceByCompositeOn(this IFilter filter, params string[] paths)
|
||||||
|
{
|
||||||
|
var simpleFilter = filter as ISimpleFilter;
|
||||||
|
if (simpleFilter == null)
|
||||||
|
throw new Exception("Must be a simple filter");
|
||||||
|
|
||||||
|
var compositeFilter = new CompositeFilter();
|
||||||
|
compositeFilter.And = filter.And;
|
||||||
|
compositeFilter.Type = FilterType.Composite;
|
||||||
|
compositeFilter.Filters = paths
|
||||||
|
.Select(t => new SimpleFilter
|
||||||
|
{
|
||||||
|
Type = filter.Type,
|
||||||
|
Path = t,
|
||||||
|
And = false,
|
||||||
|
Value = simpleFilter.Value
|
||||||
|
})
|
||||||
|
.AsEnumerable<IFilter>()
|
||||||
|
.ToList();
|
||||||
|
return compositeFilter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ICompositeFilter ReplaceByCompositeOn<T>(this IFilter filter, params Expression<Func<T, object>>[] exprs)
|
||||||
|
{
|
||||||
|
var paths = exprs.Select(expr => GetPropertySymbol(expr)).ToArray();
|
||||||
|
return ReplaceByCompositeOn(filter, paths);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static string GetPropertySymbol<T, TResult>(Expression<Func<T, TResult>> expression)
|
||||||
|
{
|
||||||
|
return string.Join(".",
|
||||||
|
GetMembersOnPath(expression.Body as MemberExpression)
|
||||||
|
.Select(m => m.Member.Name)
|
||||||
|
.Reverse());
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IEnumerable<MemberExpression> GetMembersOnPath(MemberExpression expression)
|
||||||
|
{
|
||||||
|
while (expression != null)
|
||||||
|
{
|
||||||
|
yield return expression;
|
||||||
|
expression = expression.Expression as MemberExpression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,7 @@
|
|||||||
<RepositoryUrl>https://github.com/PoweredSoft/DynamicQuery</RepositoryUrl>
|
<RepositoryUrl>https://github.com/PoweredSoft/DynamicQuery</RepositoryUrl>
|
||||||
<RepositoryType>github</RepositoryType>
|
<RepositoryType>github</RepositoryType>
|
||||||
<PackageTags>powered,soft,dynamic,criteria,query,builder</PackageTags>
|
<PackageTags>powered,soft,dynamic,criteria,query,builder</PackageTags>
|
||||||
<Version>1.0.4</Version>
|
<Version>1.0.5</Version>
|
||||||
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;r=g&amp;d=retro</PackageIconUrl>
|
<PackageIconUrl>https://secure.gravatar.com/avatar/4e32f73820c16718909a06c2927f1f8b?s=512&amp;r=g&amp;d=retro</PackageIconUrl>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user