mirror of
				https://github.com/Biarity/Sieve.git
				synced 2025-11-03 18:29:02 +01:00 
			
		
		
		
	Merge pull request #51 from Nekromancer/master
Allow filtering and sorting for nested objects
This commit is contained in:
		@@ -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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user