mirror of
https://github.com/Biarity/Sieve.git
synced 2024-11-24 06:23:02 +01:00
Merge pull request #51 from Nekromancer/master
Allow filtering and sorting for nested objects
This commit is contained in:
commit
6413f70385
@ -11,6 +11,8 @@ namespace Sieve.Attributes
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
public string FullName => Name;
|
||||
|
||||
public bool CanSort { get; set; }
|
||||
public bool CanFilter { get; set; }
|
||||
}
|
||||
|
@ -1,23 +1,34 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Sieve.Extensions
|
||||
{
|
||||
public static partial class LinqExtentions
|
||||
{
|
||||
public static IQueryable<TEntity> OrderByDynamic<TEntity>(this IQueryable<TEntity> source, string orderByProperty,
|
||||
public static IQueryable<TEntity> OrderByDynamic<TEntity>(this IQueryable<TEntity> source, string fullPropertyName, PropertyInfo propertyInfo,
|
||||
bool desc, bool useThenBy)
|
||||
{
|
||||
string command = desc ?
|
||||
( useThenBy ? "ThenByDescending" : "OrderByDescending") :
|
||||
( useThenBy ? "ThenBy" : "OrderBy");
|
||||
var type = typeof(TEntity);
|
||||
var property = type.GetProperty(orderByProperty);
|
||||
var parameter = Expression.Parameter(type, "p");
|
||||
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
|
||||
|
||||
dynamic propertyValue = parameter;
|
||||
if (fullPropertyName.Contains("."))
|
||||
{
|
||||
var parts = fullPropertyName.Split('.');
|
||||
for (var i = 0; i < parts.Length - 1; i++)
|
||||
{
|
||||
propertyValue = Expression.PropertyOrField(propertyValue, parts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var propertyAccess = Expression.MakeMemberAccess(propertyValue, propertyInfo);
|
||||
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
|
||||
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
|
||||
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, propertyInfo.PropertyType },
|
||||
source.Expression, Expression.Quote(orderByExpression));
|
||||
return source.Provider.CreateQuery<TEntity>(resultExpression);
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
public interface ISievePropertyMetadata
|
||||
{
|
||||
string Name { get; set; }
|
||||
string FullName { get; }
|
||||
bool CanFilter { get; set; }
|
||||
bool CanSort { get; set; }
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
public class SievePropertyMetadata : ISievePropertyMetadata
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string FullName { get; set; }
|
||||
public bool CanFilter { get; set; }
|
||||
public bool CanSort { get; set; }
|
||||
}
|
||||
|
@ -176,11 +176,16 @@ namespace Sieve.Services
|
||||
Expression innerExpression = null;
|
||||
foreach (var filterTermName in filterTerm.Names)
|
||||
{
|
||||
var property = GetSieveProperty<TEntity>(false, true, filterTermName);
|
||||
var (fullName, property) = GetSieveProperty<TEntity>(false, true, filterTermName);
|
||||
if (property != null)
|
||||
{
|
||||
var converter = TypeDescriptor.GetConverter(property.PropertyType);
|
||||
dynamic propertyValue = Expression.PropertyOrField(parameterExpression, property.Name);
|
||||
|
||||
dynamic propertyValue = parameterExpression;
|
||||
foreach (var part in fullName.Split('.'))
|
||||
{
|
||||
propertyValue = Expression.PropertyOrField(propertyValue, part);
|
||||
}
|
||||
|
||||
foreach (var filterTermValue in filterTerm.Values)
|
||||
{
|
||||
@ -300,11 +305,11 @@ namespace Sieve.Services
|
||||
var useThenBy = false;
|
||||
foreach (var sortTerm in model.GetSortsParsed())
|
||||
{
|
||||
var property = GetSieveProperty<TEntity>(true, false, sortTerm.Name);
|
||||
var (fullName, property) = GetSieveProperty<TEntity>(true, false, sortTerm.Name);
|
||||
|
||||
if (property != null)
|
||||
{
|
||||
result = result.OrderByDynamic(property.Name, sortTerm.Descending, useThenBy);
|
||||
result = result.OrderByDynamic(fullName, property, sortTerm.Descending, useThenBy);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -345,13 +350,19 @@ namespace Sieve.Services
|
||||
return mapper;
|
||||
}
|
||||
|
||||
private PropertyInfo GetSieveProperty<TEntity>(
|
||||
private (string, PropertyInfo) GetSieveProperty<TEntity>(
|
||||
bool canSortRequired,
|
||||
bool canFilterRequired,
|
||||
string name)
|
||||
{
|
||||
return mapper.FindProperty<TEntity>(canSortRequired, canFilterRequired, name, _options.Value.CaseSensitive)
|
||||
?? FindPropertyBySieveAttribute<TEntity>(canSortRequired, canFilterRequired, name, _options.Value.CaseSensitive);
|
||||
var property = mapper.FindProperty<TEntity>(canSortRequired, canFilterRequired, name, _options.Value.CaseSensitive);
|
||||
if(property.Item1 == null)
|
||||
{
|
||||
var prop = FindPropertyBySieveAttribute<TEntity>(canSortRequired, canFilterRequired, name, _options.Value.CaseSensitive);
|
||||
return (prop?.Name, prop);
|
||||
}
|
||||
return property;
|
||||
|
||||
}
|
||||
|
||||
private PropertyInfo FindPropertyBySieveAttribute<TEntity>(
|
||||
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Sieve.Services
|
||||
{
|
||||
@ -30,13 +31,14 @@ namespace Sieve.Services
|
||||
public PropertyFluentApi(SievePropertyMapper sievePropertyMapper, Expression<Func<TEntity, object>> expression)
|
||||
{
|
||||
_sievePropertyMapper = sievePropertyMapper;
|
||||
_property = GetPropertyInfo(expression);
|
||||
_name = _property.Name;
|
||||
(_fullName, _property) = GetPropertyInfo(expression);
|
||||
_name = _fullName;
|
||||
_canFilter = false;
|
||||
_canSort = false;
|
||||
}
|
||||
|
||||
private string _name;
|
||||
private readonly string _fullName;
|
||||
private bool _canFilter;
|
||||
private bool _canSort;
|
||||
|
||||
@ -66,12 +68,13 @@ namespace Sieve.Services
|
||||
_sievePropertyMapper._map[typeof(TEntity)][_property] = new SievePropertyMetadata()
|
||||
{
|
||||
Name = _name,
|
||||
FullName = _fullName,
|
||||
CanFilter = _canFilter,
|
||||
CanSort = _canSort
|
||||
};
|
||||
}
|
||||
|
||||
private static PropertyInfo GetPropertyInfo(Expression<Func<TEntity, object>> exp)
|
||||
private static (string, PropertyInfo) GetPropertyInfo(Expression<Func<TEntity, object>> exp)
|
||||
{
|
||||
if (!(exp.Body is MemberExpression body))
|
||||
{
|
||||
@ -79,11 +82,19 @@ namespace Sieve.Services
|
||||
body = ubody.Operand as MemberExpression;
|
||||
}
|
||||
|
||||
return body?.Member as PropertyInfo;
|
||||
var member = body?.Member as PropertyInfo;
|
||||
var stack = new Stack<string>();
|
||||
while (body != null)
|
||||
{
|
||||
stack.Push(body.Member.Name);
|
||||
body = body.Expression as MemberExpression;
|
||||
}
|
||||
|
||||
return (string.Join(".", stack.ToArray()), member);
|
||||
}
|
||||
}
|
||||
|
||||
public PropertyInfo FindProperty<TEntity>(
|
||||
public (string, PropertyInfo) FindProperty<TEntity>(
|
||||
bool canSortRequired,
|
||||
bool canFilterRequired,
|
||||
string name,
|
||||
@ -91,15 +102,17 @@ namespace Sieve.Services
|
||||
{
|
||||
try
|
||||
{
|
||||
return _map[typeof(TEntity)]
|
||||
var result = _map[typeof(TEntity)]
|
||||
.FirstOrDefault(kv =>
|
||||
kv.Value.Name.Equals(name, isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)
|
||||
&& (canSortRequired ? kv.Value.CanSort : true)
|
||||
&& (canFilterRequired ? kv.Value.CanFilter : true)).Key;
|
||||
&& (canFilterRequired ? kv.Value.CanFilter : true));
|
||||
|
||||
return (result.Value?.FullName, result.Key);
|
||||
}
|
||||
catch (Exception ex) when (ex is KeyNotFoundException || ex is ArgumentNullException)
|
||||
{
|
||||
return null;
|
||||
return (null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user