mirror of
				https://github.com/Biarity/Sieve.git
				synced 2025-11-04 10:49:09 +01:00 
			
		
		
		
	Fix for Issue #19
This commit is contained in:
		@@ -1,11 +1,9 @@
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Attributes
 | 
			
		||||
{
 | 
			
		||||
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
 | 
			
		||||
	[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
 | 
			
		||||
    public class SieveAttribute : Attribute, ISievePropertyMetadata
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,4 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Exceptions
 | 
			
		||||
{
 | 
			
		||||
@@ -13,5 +11,13 @@ namespace Sieve.Exceptions
 | 
			
		||||
        public SieveException(string message, Exception innerException) : base(message, innerException)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SieveException()
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected SieveException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,4 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Exceptions
 | 
			
		||||
{
 | 
			
		||||
@@ -10,12 +8,11 @@ namespace Sieve.Exceptions
 | 
			
		||||
        public Type ExpectedType { get; protected set; }
 | 
			
		||||
        public Type ActualType { get; protected set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public SieveIncompatibleMethodException(
 | 
			
		||||
            string methodName, 
 | 
			
		||||
            string methodName,
 | 
			
		||||
            Type expectedType,
 | 
			
		||||
            Type actualType,
 | 
			
		||||
            string message) 
 | 
			
		||||
            string message)
 | 
			
		||||
            : base(message)
 | 
			
		||||
        {
 | 
			
		||||
            MethodName = methodName;
 | 
			
		||||
@@ -24,16 +21,32 @@ namespace Sieve.Exceptions
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SieveIncompatibleMethodException(
 | 
			
		||||
            string methodName,                        
 | 
			
		||||
            string methodName,
 | 
			
		||||
            Type expectedType,
 | 
			
		||||
            Type actualType,
 | 
			
		||||
            string message,
 | 
			
		||||
            Exception innerException) 
 | 
			
		||||
            Exception innerException)
 | 
			
		||||
            : base(message, innerException)
 | 
			
		||||
        {
 | 
			
		||||
            MethodName = methodName;
 | 
			
		||||
            ExpectedType = expectedType;
 | 
			
		||||
            ActualType = actualType;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SieveIncompatibleMethodException(string message) : base(message)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SieveIncompatibleMethodException(string message, Exception innerException) : base(message, innerException)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SieveIncompatibleMethodException()
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected SieveIncompatibleMethodException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,4 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Exceptions
 | 
			
		||||
{
 | 
			
		||||
@@ -8,7 +6,7 @@ namespace Sieve.Exceptions
 | 
			
		||||
    {
 | 
			
		||||
        public string MethodName { get; protected set; }
 | 
			
		||||
 | 
			
		||||
        public SieveMethodNotFoundException(string methodName, string message) : base (message) 
 | 
			
		||||
        public SieveMethodNotFoundException(string methodName, string message) : base(message)
 | 
			
		||||
        {
 | 
			
		||||
            MethodName = methodName;
 | 
			
		||||
        }
 | 
			
		||||
@@ -17,5 +15,21 @@ namespace Sieve.Exceptions
 | 
			
		||||
        {
 | 
			
		||||
            MethodName = methodName;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SieveMethodNotFoundException(string message) : base(message)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SieveMethodNotFoundException(string message, Exception innerException) : base(message, innerException)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SieveMethodNotFoundException()
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected SieveMethodNotFoundException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,15 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Extensions
 | 
			
		||||
{
 | 
			
		||||
    public static partial class LinqExtentions
 | 
			
		||||
	public static partial class LinqExtentions
 | 
			
		||||
    {
 | 
			
		||||
        public static IQueryable<TEntity> OrderByDynamic<TEntity>(this IQueryable<TEntity> source, string orderByProperty,
 | 
			
		||||
                          bool desc, bool useThenBy)
 | 
			
		||||
        {
 | 
			
		||||
            string command = desc ? 
 | 
			
		||||
            string command = desc ?
 | 
			
		||||
                ( useThenBy ? "ThenByDescending" : "OrderByDescending") :
 | 
			
		||||
                ( useThenBy ? "ThenBy" : "OrderBy");
 | 
			
		||||
            var type = typeof(TEntity);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
{
 | 
			
		||||
    public enum FilterOperator
 | 
			
		||||
	public enum FilterOperator
 | 
			
		||||
    {
 | 
			
		||||
        Equals,
 | 
			
		||||
        NotEquals,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,11 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
{
 | 
			
		||||
    public class FilterTerm : IFilterTerm
 | 
			
		||||
    {
 | 
			
		||||
        private string _filter;
 | 
			
		||||
        private string[] operators = new string[] {
 | 
			
		||||
        private static readonly string[] Operators = new string[] {
 | 
			
		||||
                    "==*",
 | 
			
		||||
                    "@=*",
 | 
			
		||||
                    "_=*",
 | 
			
		||||
@@ -24,82 +21,50 @@ namespace Sieve.Models
 | 
			
		||||
 | 
			
		||||
        public FilterTerm(string filter)
 | 
			
		||||
        {
 | 
			
		||||
            _filter = filter;
 | 
			
		||||
            var filterSplits = filter.Split(Operators, StringSplitOptions.RemoveEmptyEntries).Select(t => t.Trim()).ToArray();
 | 
			
		||||
            Names = filterSplits[0].Split('|').Select(t => t.Trim()).ToArray();
 | 
			
		||||
            Value = filterSplits.Length > 1 ? filterSplits[1] : null;
 | 
			
		||||
            Operator = Array.Find(Operators, o => filter.Contains(o)) ?? "==";
 | 
			
		||||
            OperatorParsed = GetOperatorParsed(Operator);
 | 
			
		||||
            OperatorIsCaseInsensitive = Operator.Contains("*");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Name
 | 
			
		||||
        public string[] Names { get; }
 | 
			
		||||
 | 
			
		||||
        public FilterOperator OperatorParsed { get; }
 | 
			
		||||
 | 
			
		||||
        public string Value { get; }
 | 
			
		||||
 | 
			
		||||
        public string Operator { get; }
 | 
			
		||||
 | 
			
		||||
        private FilterOperator GetOperatorParsed(string Operator)
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            switch (Operator.Trim().ToLower())
 | 
			
		||||
            {
 | 
			
		||||
                var tokens = _filter.Split(operators, StringSplitOptions.RemoveEmptyEntries);
 | 
			
		||||
                return tokens.Length > 0 ? tokens[0].Trim() : "";
 | 
			
		||||
                
 | 
			
		||||
                case "==":
 | 
			
		||||
                case "==*":
 | 
			
		||||
                    return FilterOperator.Equals;
 | 
			
		||||
                case "!=":
 | 
			
		||||
                    return FilterOperator.NotEquals;
 | 
			
		||||
                case "<":
 | 
			
		||||
                    return FilterOperator.LessThan;
 | 
			
		||||
                case ">":
 | 
			
		||||
                    return FilterOperator.GreaterThan;
 | 
			
		||||
                case ">=":
 | 
			
		||||
                    return FilterOperator.GreaterThanOrEqualTo;
 | 
			
		||||
                case "<=":
 | 
			
		||||
                    return FilterOperator.LessThanOrEqualTo;
 | 
			
		||||
                case "@=":
 | 
			
		||||
                case "@=*":
 | 
			
		||||
                    return FilterOperator.Contains;
 | 
			
		||||
                case "_=":
 | 
			
		||||
                case "_=*":
 | 
			
		||||
                    return FilterOperator.StartsWith;
 | 
			
		||||
                default:
 | 
			
		||||
                    return FilterOperator.Equals;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Operator
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var op in operators)
 | 
			
		||||
                {
 | 
			
		||||
                    if (_filter.IndexOf(op) != -1)
 | 
			
		||||
                    {
 | 
			
		||||
                        return op;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Custom operators not supported
 | 
			
		||||
                // var tokens = _filter.Split(' ');
 | 
			
		||||
                // return tokens.Length > 2 ? tokens[1] : "";
 | 
			
		||||
                return "";
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Value {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                var tokens = _filter.Split(operators, StringSplitOptions.RemoveEmptyEntries);
 | 
			
		||||
                return tokens.Length > 1 ? tokens[1].Trim() : null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public FilterOperator OperatorParsed {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                switch (Operator.Trim().ToLower())
 | 
			
		||||
                {
 | 
			
		||||
                    case "==":
 | 
			
		||||
                    case "==*":
 | 
			
		||||
                        return FilterOperator.Equals;
 | 
			
		||||
                    case "!=":
 | 
			
		||||
                        return FilterOperator.NotEquals;
 | 
			
		||||
                    case "<":
 | 
			
		||||
                        return FilterOperator.LessThan;
 | 
			
		||||
                    case ">":
 | 
			
		||||
                        return FilterOperator.GreaterThan;
 | 
			
		||||
                    case ">=":
 | 
			
		||||
                        return FilterOperator.GreaterThanOrEqualTo;
 | 
			
		||||
                    case "<=":
 | 
			
		||||
                        return FilterOperator.LessThanOrEqualTo;
 | 
			
		||||
                    case "@=":
 | 
			
		||||
                    case "@=*":
 | 
			
		||||
                        return FilterOperator.Contains;
 | 
			
		||||
                    case "_=":
 | 
			
		||||
                    case "_=*":
 | 
			
		||||
                        return FilterOperator.StartsWith;
 | 
			
		||||
                    default:
 | 
			
		||||
                        return FilterOperator.Equals;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool OperatorIsCaseInsensitive
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return Operator.Contains("*");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public bool OperatorIsCaseInsensitive { get; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +1,8 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
{
 | 
			
		||||
    public interface IFilterTerm
 | 
			
		||||
    {
 | 
			
		||||
        string Name { get; }
 | 
			
		||||
        string[] Names { get; }
 | 
			
		||||
        string Operator { get; }
 | 
			
		||||
        bool OperatorIsCaseInsensitive { get; }
 | 
			
		||||
        FilterOperator OperatorParsed { get; }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,17 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
{
 | 
			
		||||
    public interface ISieveModel<TFilterTerm, TSortTerm> 
 | 
			
		||||
	public interface ISieveModel<TFilterTerm, TSortTerm>
 | 
			
		||||
        where TFilterTerm : IFilterTerm
 | 
			
		||||
        where TSortTerm : ISortTerm
 | 
			
		||||
    {
 | 
			
		||||
        string Filters { get; set; }
 | 
			
		||||
 | 
			
		||||
        string Sorts { get; set; }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        int? Page { get; set; }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        int? PageSize { get; set; }
 | 
			
		||||
 | 
			
		||||
        List<TFilterTerm> FiltersParsed { get; }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
{
 | 
			
		||||
    public interface ISievePropertyMetadata
 | 
			
		||||
	public interface ISievePropertyMetadata
 | 
			
		||||
    {
 | 
			
		||||
        string Name { get; set; }
 | 
			
		||||
        bool CanFilter { get; set; }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
{
 | 
			
		||||
    public interface ISortTerm
 | 
			
		||||
	public interface ISortTerm
 | 
			
		||||
    {
 | 
			
		||||
        bool Descending { get; }
 | 
			
		||||
        string Name { get; }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,20 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
{
 | 
			
		||||
    public class SieveModel: ISieveModel<IFilterTerm, ISortTerm>
 | 
			
		||||
    public class SieveModel : ISieveModel<IFilterTerm, ISortTerm>
 | 
			
		||||
    {
 | 
			
		||||
        public string Filters { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Sorts { get; set; }
 | 
			
		||||
 | 
			
		||||
        [Range(1, Double.MaxValue)]
 | 
			
		||||
        [Range(1, int.MaxValue)]
 | 
			
		||||
        public int? Page { get; set; }
 | 
			
		||||
 | 
			
		||||
        [Range(1, Double.MaxValue)]
 | 
			
		||||
        [Range(1, int.MaxValue)]
 | 
			
		||||
        public int? PageSize { get; set; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        public List<IFilterTerm> FiltersParsed
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
@@ -30,13 +27,10 @@ namespace Sieve.Models
 | 
			
		||||
                        if (filter.StartsWith("("))
 | 
			
		||||
                        {
 | 
			
		||||
                            var filterOpAndVal = filter.Substring(filter.LastIndexOf(")") + 1);
 | 
			
		||||
                            var subfilters = filter.Replace(filterOpAndVal, "").Replace("(", "").Replace(")","");
 | 
			
		||||
                            foreach (var subfilter in subfilters.Split('|'))
 | 
			
		||||
                            {
 | 
			
		||||
                                value.Add(new FilterTerm(subfilter + filterOpAndVal));
 | 
			
		||||
                            }
 | 
			
		||||
                            var subfilters = filter.Replace(filterOpAndVal, "").Replace("(", "").Replace(")", "");
 | 
			
		||||
                            value.Add(new FilterTerm(subfilters + filterOpAndVal));
 | 
			
		||||
                        }
 | 
			
		||||
                        else 
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            value.Add(new FilterTerm(filter));
 | 
			
		||||
                        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,6 @@
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
{
 | 
			
		||||
    public class SieveOptions
 | 
			
		||||
	public class SieveOptions
 | 
			
		||||
    {
 | 
			
		||||
        public bool CaseSensitive { get; set; } = false;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
{
 | 
			
		||||
    public class SievePropertyMetadata : ISievePropertyMetadata
 | 
			
		||||
	public class SievePropertyMetadata : ISievePropertyMetadata
 | 
			
		||||
    {
 | 
			
		||||
        public string Name { get; set; }
 | 
			
		||||
        public bool CanFilter { get; set; }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,47 +1,16 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.ComponentModel.DataAnnotations;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
namespace Sieve.Models
 | 
			
		||||
{
 | 
			
		||||
    public class SortTerm : ISortTerm
 | 
			
		||||
    {
 | 
			
		||||
        private string _sort;
 | 
			
		||||
        private readonly string _sort;
 | 
			
		||||
 | 
			
		||||
        public SortTerm(string sort)
 | 
			
		||||
        {
 | 
			
		||||
            _sort = sort;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Name
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (_sort.StartsWith("-"))
 | 
			
		||||
                {
 | 
			
		||||
                    return _sort.Substring(1);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return _sort;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public string Name => (_sort.StartsWith("-")) ? _sort.Substring(1) : _sort;
 | 
			
		||||
 | 
			
		||||
        public bool Descending
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                if (_sort.StartsWith("-"))
 | 
			
		||||
                {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public bool Descending => _sort.StartsWith("-");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,10 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Services
 | 
			
		||||
namespace Sieve.Services
 | 
			
		||||
{
 | 
			
		||||
    public interface ISieveCustomFilterMethods
 | 
			
		||||
	public interface ISieveCustomFilterMethods
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Services
 | 
			
		||||
namespace Sieve.Services
 | 
			
		||||
{
 | 
			
		||||
    public interface ISieveCustomSortMethods
 | 
			
		||||
	public interface ISieveCustomSortMethods
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,13 @@
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Services
 | 
			
		||||
{
 | 
			
		||||
    public interface ISieveProcessor
 | 
			
		||||
	public interface ISieveProcessor
 | 
			
		||||
    {
 | 
			
		||||
        IQueryable<TEntity> Apply<TEntity>(
 | 
			
		||||
            ISieveModel<IFilterTerm, ISortTerm> model, 
 | 
			
		||||
            IQueryable<TEntity> source, 
 | 
			
		||||
            ISieveModel<IFilterTerm, ISortTerm> model,
 | 
			
		||||
            IQueryable<TEntity> source,
 | 
			
		||||
            object[] dataForCustomMethods = null,
 | 
			
		||||
            bool applyFiltering = true,
 | 
			
		||||
            bool applySorting = true,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,22 @@
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using Sieve.Attributes;
 | 
			
		||||
using Sieve.Extensions;
 | 
			
		||||
using System;
 | 
			
		||||
using System.ComponentModel;
 | 
			
		||||
using System.Collections;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using Sieve.Attributes;
 | 
			
		||||
using Sieve.Exceptions;
 | 
			
		||||
using Sieve.Extensions;
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Services
 | 
			
		||||
{
 | 
			
		||||
    public class SieveProcessor : ISieveProcessor
 | 
			
		||||
    {
 | 
			
		||||
        private IOptions<SieveOptions> _options;
 | 
			
		||||
        private ISieveCustomSortMethods _customSortMethods;
 | 
			
		||||
        private ISieveCustomFilterMethods _customFilterMethods;
 | 
			
		||||
        private SievePropertyMapper mapper = new SievePropertyMapper();
 | 
			
		||||
        
 | 
			
		||||
        private readonly IOptions<SieveOptions> _options;
 | 
			
		||||
        private readonly ISieveCustomSortMethods _customSortMethods;
 | 
			
		||||
        private readonly ISieveCustomFilterMethods _customFilterMethods;
 | 
			
		||||
        private readonly SievePropertyMapper mapper = new SievePropertyMapper();
 | 
			
		||||
 | 
			
		||||
        public SieveProcessor(IOptions<SieveOptions> options,
 | 
			
		||||
            ISieveCustomSortMethods customSortMethods,
 | 
			
		||||
@@ -67,8 +62,8 @@ namespace Sieve.Services
 | 
			
		||||
        /// <param name="applyPagination">Should the data be paginated? Defaults to true.</param>
 | 
			
		||||
        /// <returns>Returns a transformed version of `source`</returns>
 | 
			
		||||
        public IQueryable<TEntity> Apply<TEntity>(
 | 
			
		||||
            ISieveModel<IFilterTerm, ISortTerm> model, 
 | 
			
		||||
            IQueryable<TEntity> source, 
 | 
			
		||||
            ISieveModel<IFilterTerm, ISortTerm> model,
 | 
			
		||||
            IQueryable<TEntity> source,
 | 
			
		||||
            object[] dataForCustomMethods = null,
 | 
			
		||||
            bool applyFiltering = true,
 | 
			
		||||
            bool applySorting = true,
 | 
			
		||||
@@ -77,21 +72,29 @@ namespace Sieve.Services
 | 
			
		||||
            var result = source;
 | 
			
		||||
 | 
			
		||||
            if (model == null)
 | 
			
		||||
            {
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                // Filter
 | 
			
		||||
                if (applyFiltering)
 | 
			
		||||
                {
 | 
			
		||||
                    result = ApplyFiltering(model, result, dataForCustomMethods);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Sort
 | 
			
		||||
                if (applySorting)
 | 
			
		||||
                {
 | 
			
		||||
                    result = ApplySorting(model, result, dataForCustomMethods);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Paginate
 | 
			
		||||
                if (applyPagination)
 | 
			
		||||
                {
 | 
			
		||||
                    result = ApplyPagination(model, result);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
@@ -100,7 +103,10 @@ namespace Sieve.Services
 | 
			
		||||
                if (_options.Value.ThrowExceptions)
 | 
			
		||||
                {
 | 
			
		||||
                    if (ex is SieveException)
 | 
			
		||||
                    {
 | 
			
		||||
                        throw;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    throw new SieveException(ex.Message, ex);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
@@ -109,105 +115,117 @@ namespace Sieve.Services
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        private IQueryable<TEntity> ApplyFiltering<TEntity>(
 | 
			
		||||
            ISieveModel<IFilterTerm, ISortTerm> model, 
 | 
			
		||||
            IQueryable<TEntity> result, 
 | 
			
		||||
            ISieveModel<IFilterTerm, ISortTerm> model,
 | 
			
		||||
            IQueryable<TEntity> result,
 | 
			
		||||
            object[] dataForCustomMethods = null)
 | 
			
		||||
        {
 | 
			
		||||
            if (model?.FiltersParsed == null)
 | 
			
		||||
                return result;
 | 
			
		||||
 | 
			
		||||
            foreach (var filterTerm in model.FiltersParsed)
 | 
			
		||||
            {
 | 
			
		||||
                var property = GetSieveProperty<TEntity>(false, true, filterTerm.Name);
 | 
			
		||||
 | 
			
		||||
                if (property != null)
 | 
			
		||||
                {
 | 
			
		||||
                    var converter = TypeDescriptor.GetConverter(property.PropertyType);
 | 
			
		||||
                    var parameter = Expression.Parameter(typeof(TEntity), "e");
 | 
			
		||||
 | 
			
		||||
                    dynamic filterValue = Expression.Constant(
 | 
			
		||||
                        converter.CanConvertFrom(typeof(string)) ?
 | 
			
		||||
                        converter.ConvertFrom(filterTerm.Value) :
 | 
			
		||||
                        Convert.ChangeType(filterTerm.Value, property.PropertyType));
 | 
			
		||||
 | 
			
		||||
                    dynamic propertyValue = Expression.PropertyOrField(parameter, property.Name);
 | 
			
		||||
 | 
			
		||||
                    if (filterTerm.OperatorIsCaseInsensitive)
 | 
			
		||||
                    {
 | 
			
		||||
                        propertyValue = Expression.Call(propertyValue,
 | 
			
		||||
                            typeof(string).GetMethods()
 | 
			
		||||
                            .First(m => m.Name == "ToUpper" && m.GetParameters().Length == 0));
 | 
			
		||||
 | 
			
		||||
                        filterValue = Expression.Call(filterValue,
 | 
			
		||||
                            typeof(string).GetMethods()
 | 
			
		||||
                            .First(m => m.Name == "ToUpper" && m.GetParameters().Length == 0));
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    Expression comparison;
 | 
			
		||||
 | 
			
		||||
                    switch (filterTerm.OperatorParsed)
 | 
			
		||||
                    {
 | 
			
		||||
                        case FilterOperator.Equals:
 | 
			
		||||
                            comparison = Expression.Equal(propertyValue, filterValue);
 | 
			
		||||
                            break;
 | 
			
		||||
                        case FilterOperator.NotEquals:
 | 
			
		||||
                            comparison = Expression.NotEqual(propertyValue, filterValue);
 | 
			
		||||
                            break;
 | 
			
		||||
                        case FilterOperator.GreaterThan:
 | 
			
		||||
                            comparison = Expression.GreaterThan(propertyValue, filterValue);
 | 
			
		||||
                            break;
 | 
			
		||||
                        case FilterOperator.LessThan:
 | 
			
		||||
                            comparison = Expression.LessThan(propertyValue, filterValue);
 | 
			
		||||
                            break;
 | 
			
		||||
                        case FilterOperator.GreaterThanOrEqualTo:
 | 
			
		||||
                            comparison = Expression.GreaterThanOrEqual(propertyValue, filterValue);
 | 
			
		||||
                            break;
 | 
			
		||||
                        case FilterOperator.LessThanOrEqualTo:
 | 
			
		||||
                            comparison = Expression.LessThanOrEqual(propertyValue, filterValue);
 | 
			
		||||
                            break;
 | 
			
		||||
                        case FilterOperator.Contains:
 | 
			
		||||
                            comparison = Expression.Call(propertyValue,
 | 
			
		||||
                                typeof(string).GetMethods()
 | 
			
		||||
                                .First(m => m.Name == "Contains" && m.GetParameters().Length == 1),
 | 
			
		||||
                                filterValue);
 | 
			
		||||
                            break;
 | 
			
		||||
                        case FilterOperator.StartsWith:
 | 
			
		||||
                            comparison = Expression.Call(propertyValue,
 | 
			
		||||
                                typeof(string).GetMethods()
 | 
			
		||||
                                .First(m => m.Name == "StartsWith" && m.GetParameters().Length == 1),
 | 
			
		||||
                                filterValue); break;
 | 
			
		||||
                        default:
 | 
			
		||||
                            comparison = Expression.Equal(propertyValue, filterValue);
 | 
			
		||||
                            break;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    result = result.Where(Expression.Lambda<Func<TEntity, bool>>(
 | 
			
		||||
                                comparison,
 | 
			
		||||
                                parameter));
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    result = ApplyCustomMethod(result, filterTerm.Name, _customFilterMethods,
 | 
			
		||||
                        new object[] {
 | 
			
		||||
                        result,
 | 
			
		||||
                        filterTerm.Operator,
 | 
			
		||||
                        filterTerm.Value
 | 
			
		||||
                        }, dataForCustomMethods);
 | 
			
		||||
                }
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
            Expression outerExpression = null;
 | 
			
		||||
            var parameterExpression = Expression.Parameter(typeof(TEntity), "e");
 | 
			
		||||
            foreach (var filterTerm in model.FiltersParsed)
 | 
			
		||||
            {
 | 
			
		||||
                Expression innerExpression = null;
 | 
			
		||||
                foreach (var filterTermName in filterTerm.Names)
 | 
			
		||||
                {
 | 
			
		||||
                    var property = GetSieveProperty<TEntity>(false, true, filterTermName);
 | 
			
		||||
                    if (property != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        var converter = TypeDescriptor.GetConverter(property.PropertyType);
 | 
			
		||||
                        dynamic filterValue = Expression.Constant(
 | 
			
		||||
                            converter.CanConvertFrom(typeof(string))
 | 
			
		||||
                                ? converter.ConvertFrom(filterTerm.Value)
 | 
			
		||||
                                : Convert.ChangeType(filterTerm.Value, property.PropertyType));
 | 
			
		||||
 | 
			
		||||
                        dynamic propertyValue = Expression.PropertyOrField(parameterExpression, property.Name);
 | 
			
		||||
 | 
			
		||||
                        if (filterTerm.OperatorIsCaseInsensitive)
 | 
			
		||||
                        {
 | 
			
		||||
                            propertyValue = Expression.Call(propertyValue,
 | 
			
		||||
                                typeof(string).GetMethods()
 | 
			
		||||
                                .First(m => m.Name == "ToUpper" && m.GetParameters().Length == 0));
 | 
			
		||||
 | 
			
		||||
                            filterValue = Expression.Call(filterValue,
 | 
			
		||||
                                typeof(string).GetMethods()
 | 
			
		||||
                                .First(m => m.Name == "ToUpper" && m.GetParameters().Length == 0));
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        if (innerExpression == null)
 | 
			
		||||
                        {
 | 
			
		||||
                            innerExpression = GetExpression(filterTerm, filterValue, propertyValue);
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            innerExpression = Expression.Or(innerExpression, GetExpression(filterTerm, filterValue, propertyValue));
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        var parameters = new object[] {
 | 
			
		||||
                                            result,
 | 
			
		||||
                                            filterTerm.Operator,
 | 
			
		||||
                                            filterTerm.Value
 | 
			
		||||
                            };
 | 
			
		||||
                        result = ApplyCustomMethod(result, filterTermName, _customFilterMethods, parameters, dataForCustomMethods);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (outerExpression == null)
 | 
			
		||||
                {
 | 
			
		||||
                    outerExpression = innerExpression;
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                outerExpression = Expression.And(outerExpression, innerExpression);
 | 
			
		||||
            }
 | 
			
		||||
            return outerExpression == null
 | 
			
		||||
                ? result
 | 
			
		||||
                : result.Where(Expression.Lambda<Func<TEntity, bool>>(outerExpression, parameterExpression));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private static Expression GetExpression(IFilterTerm filterTerm, dynamic filterValue, dynamic propertyValue)
 | 
			
		||||
        {
 | 
			
		||||
            switch (filterTerm.OperatorParsed)
 | 
			
		||||
            {
 | 
			
		||||
                case FilterOperator.Equals:
 | 
			
		||||
                    return Expression.Equal(propertyValue, filterValue);
 | 
			
		||||
                case FilterOperator.NotEquals:
 | 
			
		||||
                    return Expression.NotEqual(propertyValue, filterValue);
 | 
			
		||||
                case FilterOperator.GreaterThan:
 | 
			
		||||
                    return Expression.GreaterThan(propertyValue, filterValue);
 | 
			
		||||
                case FilterOperator.LessThan:
 | 
			
		||||
                    return Expression.LessThan(propertyValue, filterValue);
 | 
			
		||||
                case FilterOperator.GreaterThanOrEqualTo:
 | 
			
		||||
                    return Expression.GreaterThanOrEqual(propertyValue, filterValue);
 | 
			
		||||
                case FilterOperator.LessThanOrEqualTo:
 | 
			
		||||
                    return Expression.LessThanOrEqual(propertyValue, filterValue);
 | 
			
		||||
                case FilterOperator.Contains:
 | 
			
		||||
                    return Expression.Call(propertyValue,
 | 
			
		||||
                        typeof(string).GetMethods()
 | 
			
		||||
                        .First(m => m.Name == "Contains" && m.GetParameters().Length == 1),
 | 
			
		||||
                        filterValue);
 | 
			
		||||
                case FilterOperator.StartsWith:
 | 
			
		||||
                    return Expression.Call(propertyValue,
 | 
			
		||||
                        typeof(string).GetMethods()
 | 
			
		||||
                        .First(m => m.Name == "StartsWith" && m.GetParameters().Length == 1),
 | 
			
		||||
                        filterValue);
 | 
			
		||||
                default:
 | 
			
		||||
                    return Expression.Equal(propertyValue, filterValue);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private IQueryable<TEntity> ApplySorting<TEntity>(
 | 
			
		||||
            ISieveModel<IFilterTerm, ISortTerm> model, 
 | 
			
		||||
            ISieveModel<IFilterTerm, ISortTerm> model,
 | 
			
		||||
            IQueryable<TEntity> result,
 | 
			
		||||
            object[] dataForCustomMethods = null)
 | 
			
		||||
        {
 | 
			
		||||
            if (model?.SortsParsed == null)
 | 
			
		||||
            {
 | 
			
		||||
                return result;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var useThenBy = false;
 | 
			
		||||
            foreach (var sortTerm in model.SortsParsed)
 | 
			
		||||
@@ -235,7 +253,7 @@ namespace Sieve.Services
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private IQueryable<TEntity> ApplyPagination<TEntity>(
 | 
			
		||||
            ISieveModel<IFilterTerm, ISortTerm> model, 
 | 
			
		||||
            ISieveModel<IFilterTerm, ISortTerm> model,
 | 
			
		||||
            IQueryable<TEntity> result)
 | 
			
		||||
        {
 | 
			
		||||
            var page = model?.Page ?? 1;
 | 
			
		||||
@@ -245,20 +263,21 @@ namespace Sieve.Services
 | 
			
		||||
            result = result.Skip((page - 1) * pageSize);
 | 
			
		||||
 | 
			
		||||
            if (pageSize > 0)
 | 
			
		||||
            {
 | 
			
		||||
                result = result.Take(Math.Min(pageSize, maxPageSize));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        protected virtual SievePropertyMapper MapProperties(SievePropertyMapper mapper)
 | 
			
		||||
        {
 | 
			
		||||
            return mapper;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private PropertyInfo GetSieveProperty<TEntity>(
 | 
			
		||||
            bool canSortRequired, 
 | 
			
		||||
            bool canFilterRequired, 
 | 
			
		||||
            bool canSortRequired,
 | 
			
		||||
            bool canFilterRequired,
 | 
			
		||||
            string name)
 | 
			
		||||
        {
 | 
			
		||||
            return mapper.FindProperty<TEntity>(canSortRequired, canFilterRequired, name, _options.Value.CaseSensitive)
 | 
			
		||||
@@ -266,27 +285,21 @@ namespace Sieve.Services
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private PropertyInfo FindPropertyBySieveAttribute<TEntity>(
 | 
			
		||||
            bool canSortRequired, 
 | 
			
		||||
            bool canFilterRequired, 
 | 
			
		||||
            bool canSortRequired,
 | 
			
		||||
            bool canFilterRequired,
 | 
			
		||||
            string name,
 | 
			
		||||
            bool isCaseSensitive)
 | 
			
		||||
        {
 | 
			
		||||
            return typeof(TEntity).GetProperties().FirstOrDefault(p =>
 | 
			
		||||
            bool isCaseSensitive) => Array.Find(typeof(TEntity).GetProperties(), p =>
 | 
			
		||||
            {
 | 
			
		||||
                if (p.GetCustomAttribute(typeof(SieveAttribute)) is SieveAttribute sieveAttribute)
 | 
			
		||||
                    if ((canSortRequired ? sieveAttribute.CanSort : true) &&
 | 
			
		||||
                        (canFilterRequired ? sieveAttribute.CanFilter : true) &&
 | 
			
		||||
                        ((sieveAttribute.Name ?? p.Name).Equals(name,
 | 
			
		||||
                            isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)))
 | 
			
		||||
                        return true;
 | 
			
		||||
                return false;
 | 
			
		||||
                return p.GetCustomAttribute(typeof(SieveAttribute)) is SieveAttribute sieveAttribute
 | 
			
		||||
                    && (canSortRequired ? sieveAttribute.CanSort : true)
 | 
			
		||||
                    && (canFilterRequired ? sieveAttribute.CanFilter : true)
 | 
			
		||||
                    && ((sieveAttribute.Name ?? p.Name).Equals(name, isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase));
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private IQueryable<TEntity> ApplyCustomMethod<TEntity>(IQueryable<TEntity> result, string name, object parent, object[] parameters, object[] optionalParameters = null)
 | 
			
		||||
        {
 | 
			
		||||
            var customMethod = parent?.GetType()
 | 
			
		||||
                .GetMethod(name, 
 | 
			
		||||
                .GetMethod(name,
 | 
			
		||||
                _options.Value.CaseSensitive ? BindingFlags.Default : BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
 | 
			
		||||
 | 
			
		||||
            if (customMethod != null)
 | 
			
		||||
@@ -308,7 +321,7 @@ namespace Sieve.Services
 | 
			
		||||
                        throw;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                catch (ArgumentException) // name matched with custom method for a differnt type
 | 
			
		||||
                catch (ArgumentException) // name matched with custom method for a different type
 | 
			
		||||
                {
 | 
			
		||||
                    var expected = typeof(IQueryable<TEntity>);
 | 
			
		||||
                    var actual = customMethod.ReturnType;
 | 
			
		||||
@@ -318,8 +331,7 @@ namespace Sieve.Services
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                throw new SieveMethodNotFoundException(name, 
 | 
			
		||||
                    $"{name} not found.");
 | 
			
		||||
                throw new SieveMethodNotFoundException(name, $"{name} not found.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,27 +4,28 @@ using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Sieve.Services
 | 
			
		||||
{
 | 
			
		||||
    public class SievePropertyMapper
 | 
			
		||||
	public class SievePropertyMapper
 | 
			
		||||
    {
 | 
			
		||||
        private Dictionary<Type, Dictionary<PropertyInfo, ISievePropertyMetadata>> _map
 | 
			
		||||
        private readonly Dictionary<Type, Dictionary<PropertyInfo, ISievePropertyMetadata>> _map
 | 
			
		||||
            = new Dictionary<Type, Dictionary<PropertyInfo, ISievePropertyMetadata>>();
 | 
			
		||||
 | 
			
		||||
        public PropertyFluentApi<TEntity> Property<TEntity>(Expression<Func<TEntity, object>> expression)
 | 
			
		||||
        {
 | 
			
		||||
            if(!_map.ContainsKey(typeof(TEntity)))
 | 
			
		||||
            {
 | 
			
		||||
                _map.Add(typeof(TEntity), new Dictionary<PropertyInfo, ISievePropertyMetadata>());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return new PropertyFluentApi<TEntity>(this, expression);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public class PropertyFluentApi<TEntity>
 | 
			
		||||
        {
 | 
			
		||||
            private SievePropertyMapper _sievePropertyMapper;
 | 
			
		||||
            private PropertyInfo _property;
 | 
			
		||||
            private readonly SievePropertyMapper _sievePropertyMapper;
 | 
			
		||||
            private readonly PropertyInfo _property;
 | 
			
		||||
 | 
			
		||||
            public PropertyFluentApi(SievePropertyMapper sievePropertyMapper, Expression<Func<TEntity, object>> expression)
 | 
			
		||||
            {
 | 
			
		||||
@@ -77,7 +78,7 @@ namespace Sieve.Services
 | 
			
		||||
                    var ubody = (UnaryExpression)exp.Body;
 | 
			
		||||
                    body = ubody.Operand as MemberExpression;
 | 
			
		||||
                }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
                return body?.Member as PropertyInfo;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -92,16 +93,14 @@ namespace Sieve.Services
 | 
			
		||||
            {
 | 
			
		||||
                return _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;
 | 
			
		||||
                    kv.Value.Name.Equals(name, isCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)
 | 
			
		||||
                    && (canSortRequired ? kv.Value.CanSort : true)
 | 
			
		||||
                    && (canFilterRequired ? kv.Value.CanFilter : true)).Key;
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex) when (ex is KeyNotFoundException || ex is ArgumentNullException)
 | 
			
		||||
            {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user