mirror of
				https://github.com/Biarity/Sieve.git
				synced 2025-11-04 02:39:00 +01:00 
			
		
		
		
	Merge pull request #20 from davidnmbond/master
Fix for Issue #19 / Issue #8
This commit is contained in:
		
							
								
								
									
										40
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								.editorconfig
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,40 @@
 | 
			
		||||
# To learn more about .editorconfig see https://aka.ms/editorconfigdocs
 | 
			
		||||
root = true
 | 
			
		||||
 | 
			
		||||
# All files
 | 
			
		||||
[*]
 | 
			
		||||
indent_style = space
 | 
			
		||||
trim_trailing_whitespace = true
 | 
			
		||||
 | 
			
		||||
# Code files
 | 
			
		||||
[*.{cs,csx,vb,vbx}]
 | 
			
		||||
indent_size = 4
 | 
			
		||||
end_of_line = crlf
 | 
			
		||||
 | 
			
		||||
# Xml files
 | 
			
		||||
[*.xml]
 | 
			
		||||
indent_size = 2
 | 
			
		||||
 | 
			
		||||
# Dotnet code style
 | 
			
		||||
[*.{cs,vb}]
 | 
			
		||||
# Organize usings
 | 
			
		||||
dotnet_sort_system_directives_first = true
 | 
			
		||||
 | 
			
		||||
# Avoid this. unless absolutely necessary
 | 
			
		||||
dotnet_style_qualification_for_field = false:suggestion
 | 
			
		||||
dotnet_style_qualification_for_property = false:suggestion
 | 
			
		||||
dotnet_style_qualification_for_method = false:suggestion
 | 
			
		||||
dotnet_style_qualification_for_event = false:suggestion
 | 
			
		||||
 | 
			
		||||
# Use language keywords instead of BCL types
 | 
			
		||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
 | 
			
		||||
dotnet_style_predefined_type_for_member_access = true:suggestion
 | 
			
		||||
 | 
			
		||||
# Naming conventions
 | 
			
		||||
dotnet_naming_style.pascal_case_style.capitalization        = pascal_case
 | 
			
		||||
# Classes, structs, methods, enums, events, properties, namespaces, delegates must be PascalCase
 | 
			
		||||
dotnet_naming_rule.general_naming.severity                  = suggestion
 | 
			
		||||
dotnet_naming_rule.general_naming.symbols                   = general
 | 
			
		||||
dotnet_naming_rule.general_naming.style                     = pascal_case_style
 | 
			
		||||
dotnet_naming_symbols.general.applicable_kinds              = class,struct,enum,property,method,event,namespace,delegate
 | 
			
		||||
dotnet_naming_symbols.general.applicable_accessibilities    = *
 | 
			
		||||
@@ -7,7 +7,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sieve", "Sieve\Sieve.csproj
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SieveTests", "SieveTests\SieveTests.csproj", "{8043D264-42A0-4275-97A1-46400C02E37E}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SieveUnitTests", "SieveUnitTests\SieveUnitTests.csproj", "{21C3082D-F40E-457F-BE2E-AA099E19E199}"
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SieveUnitTests", "SieveUnitTests\SieveUnitTests.csproj", "{21C3082D-F40E-457F-BE2E-AA099E19E199}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{904F25A9-5CBD-42AE-8422-ADAB98F4B4B7}"
 | 
			
		||||
	ProjectSection(SolutionItems) = preProject
 | 
			
		||||
		.editorconfig = .editorconfig
 | 
			
		||||
	EndProjectSection
 | 
			
		||||
EndProject
 | 
			
		||||
Global
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
 
 | 
			
		||||
@@ -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,98 +115,108 @@ 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 constantVal = converter.CanConvertFrom(typeof(string))
 | 
			
		||||
                                              ? converter.ConvertFrom(filterTerm.Value)
 | 
			
		||||
                                              : Convert.ChangeType(filterTerm.Value, property.PropertyType);
 | 
			
		||||
 | 
			
		||||
                    Expression filterValue = GetClosureOverConstant(constantVal);
 | 
			
		||||
 | 
			
		||||
                    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 constantVal = converter.CanConvertFrom(typeof(string))
 | 
			
		||||
                                                  ? converter.ConvertFrom(filterTerm.Value)
 | 
			
		||||
                                                  : Convert.ChangeType(filterTerm.Value, property.PropertyType);
 | 
			
		||||
 | 
			
		||||
                        Expression filterValue = GetClosureOverConstant(constantVal);
 | 
			
		||||
 | 
			
		||||
                        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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //Workaround to ensure that the filter value gets passed as a parameter in generated SQL from EF Core
 | 
			
		||||
@@ -212,12 +228,14 @@ namespace Sieve.Services
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
@@ -245,7 +263,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;
 | 
			
		||||
@@ -255,20 +273,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)
 | 
			
		||||
@@ -276,27 +295,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)
 | 
			
		||||
@@ -318,7 +331,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;
 | 
			
		||||
@@ -328,8 +341,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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,4 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
@@ -10,11 +7,11 @@ using SieveTests.Entities;
 | 
			
		||||
 | 
			
		||||
namespace SieveTests.Controllers
 | 
			
		||||
{
 | 
			
		||||
    [Route("api/[controller]/[action]")]
 | 
			
		||||
	[Route("api/[controller]/[action]")]
 | 
			
		||||
    public class PostsController : Controller
 | 
			
		||||
    {
 | 
			
		||||
        private ISieveProcessor _sieveProcessor;
 | 
			
		||||
        private ApplicationDbContext _dbContext;
 | 
			
		||||
        private readonly ISieveProcessor _sieveProcessor;
 | 
			
		||||
        private readonly ApplicationDbContext _dbContext;
 | 
			
		||||
 | 
			
		||||
        public PostsController(ISieveProcessor sieveProcessor,
 | 
			
		||||
            ApplicationDbContext dbContext)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,8 @@
 | 
			
		||||
using JetBrains.Annotations;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
 | 
			
		||||
namespace SieveTests.Entities
 | 
			
		||||
{
 | 
			
		||||
    public class ApplicationDbContext : DbContext
 | 
			
		||||
	public class ApplicationDbContext : DbContext
 | 
			
		||||
    {
 | 
			
		||||
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,10 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System;
 | 
			
		||||
using System.ComponentModel.DataAnnotations.Schema;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Sieve.Attributes;
 | 
			
		||||
 | 
			
		||||
namespace SieveTests.Entities
 | 
			
		||||
{
 | 
			
		||||
    public class Post
 | 
			
		||||
	public class Post
 | 
			
		||||
    {
 | 
			
		||||
        public int Id { get; set; }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Metadata;
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Metadata;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace SieveTests.Migrations
 | 
			
		||||
{
 | 
			
		||||
@@ -20,10 +19,7 @@ namespace SieveTests.Migrations
 | 
			
		||||
                    LikeCount = table.Column<int>(nullable: false),
 | 
			
		||||
                    Title = table.Column<string>(nullable: true)
 | 
			
		||||
                },
 | 
			
		||||
                constraints: table =>
 | 
			
		||||
                {
 | 
			
		||||
                    table.PrimaryKey("PK_Posts", x => x.Id);
 | 
			
		||||
                });
 | 
			
		||||
                constraints: table => table.PrimaryKey("PK_Posts", x => x.Id));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,21 +1,14 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore;
 | 
			
		||||
using Microsoft.AspNetCore;
 | 
			
		||||
using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
using Microsoft.Extensions.Configuration;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
 | 
			
		||||
namespace SieveTests
 | 
			
		||||
{
 | 
			
		||||
    public class Program
 | 
			
		||||
    public static class Program
 | 
			
		||||
    {
 | 
			
		||||
        public static void Main(string[] args)
 | 
			
		||||
        {
 | 
			
		||||
            var host = BuildWebHost(args);
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            host.Run();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,10 @@
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using SieveTests.Entities;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace SieveTests.Services
 | 
			
		||||
{
 | 
			
		||||
    public class ApplicationSieveProcessor : SieveProcessor
 | 
			
		||||
	public class ApplicationSieveProcessor : SieveProcessor
 | 
			
		||||
    {
 | 
			
		||||
        public ApplicationSieveProcessor(IOptions<SieveOptions> options, ISieveCustomSortMethods customSortMethods, ISieveCustomFilterMethods customFilterMethods) : base(options, customSortMethods, customFilterMethods)
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,12 @@
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using SieveTests.Entities;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace SieveTests.Services
 | 
			
		||||
{
 | 
			
		||||
    public class SieveCustomFilterMethods : ISieveCustomFilterMethods
 | 
			
		||||
	public class SieveCustomFilterMethods : ISieveCustomFilterMethods
 | 
			
		||||
    {
 | 
			
		||||
        public IQueryable<Post> IsNew(IQueryable<Post> source, string op, string value)
 | 
			
		||||
        {
 | 
			
		||||
            var result = source.Where(p => p.LikeCount < 100 &&
 | 
			
		||||
                                           p.CommentCount < 5);
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
        public IQueryable<Post> IsNew(IQueryable<Post> source)
 | 
			
		||||
            => source.Where(p => p.LikeCount < 100 && p.CommentCount < 5);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,15 @@
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using SieveTests.Entities;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace SieveTests.Services
 | 
			
		||||
{
 | 
			
		||||
    public class SieveCustomSortMethods : ISieveCustomSortMethods
 | 
			
		||||
    {
 | 
			
		||||
        public IQueryable<Post> Popularity(IQueryable<Post> source, bool useThenBy, bool desc)
 | 
			
		||||
        {
 | 
			
		||||
            var result = useThenBy ?
 | 
			
		||||
                ((IOrderedQueryable<Post>)source).ThenBy(p => p.LikeCount) :
 | 
			
		||||
                source.OrderBy(p => p.LikeCount)
 | 
			
		||||
        public IQueryable<Post> Popularity(IQueryable<Post> source, bool useThenBy) => useThenBy
 | 
			
		||||
            ? ((IOrderedQueryable<Post>)source).ThenBy(p => p.LikeCount)
 | 
			
		||||
            : source.OrderBy(p => p.LikeCount)
 | 
			
		||||
                .ThenBy(p => p.CommentCount)
 | 
			
		||||
                .ThenBy(p => p.DateCreated);
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,14 +1,8 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Microsoft.AspNetCore.Builder;
 | 
			
		||||
using Microsoft.AspNetCore.Builder;
 | 
			
		||||
using Microsoft.AspNetCore.Hosting;
 | 
			
		||||
using Microsoft.EntityFrameworkCore;
 | 
			
		||||
using Microsoft.Extensions.Configuration;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Microsoft.Extensions.Logging;
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using SieveTests.Entities;
 | 
			
		||||
@@ -16,7 +10,7 @@ using SieveTests.Services;
 | 
			
		||||
 | 
			
		||||
namespace SieveTests
 | 
			
		||||
{
 | 
			
		||||
    public class Startup
 | 
			
		||||
	public class Startup
 | 
			
		||||
    {
 | 
			
		||||
        public Startup(IConfiguration configuration)
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,6 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Sieve.Attributes;
 | 
			
		||||
 | 
			
		||||
namespace SieveUnitTests.Entities
 | 
			
		||||
namespace SieveUnitTests.Entities
 | 
			
		||||
{
 | 
			
		||||
    public class Comment
 | 
			
		||||
	public class Comment
 | 
			
		||||
    {
 | 
			
		||||
        public int Id { get; set; }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +1,9 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Sieve.Attributes;
 | 
			
		||||
 | 
			
		||||
namespace SieveUnitTests.Entities
 | 
			
		||||
{
 | 
			
		||||
    public class Post
 | 
			
		||||
	public class Post
 | 
			
		||||
    {
 | 
			
		||||
        public int Id { get; set; }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,20 +1,20 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
 | 
			
		||||
using Sieve.Exceptions;
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using SieveUnitTests.Entities;
 | 
			
		||||
using SieveUnitTests.Services;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Sieve.Exceptions;
 | 
			
		||||
 | 
			
		||||
namespace SieveUnitTests
 | 
			
		||||
{
 | 
			
		||||
    [TestClass]
 | 
			
		||||
    public class General
 | 
			
		||||
    {
 | 
			
		||||
        private SieveProcessor _processor;
 | 
			
		||||
        private IQueryable<Post> _posts;
 | 
			
		||||
        private readonly SieveProcessor _processor;
 | 
			
		||||
        private readonly IQueryable<Post> _posts;
 | 
			
		||||
 | 
			
		||||
        public General()
 | 
			
		||||
        {
 | 
			
		||||
@@ -43,7 +43,7 @@ namespace SieveUnitTests
 | 
			
		||||
                },
 | 
			
		||||
                new Post() {
 | 
			
		||||
                    Id = 3,
 | 
			
		||||
                    Title = "3",
 | 
			
		||||
                    Title = "D",
 | 
			
		||||
                    LikeCount = 3,
 | 
			
		||||
                    IsDraft = true
 | 
			
		||||
                },
 | 
			
		||||
@@ -86,11 +86,10 @@ namespace SieveUnitTests
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var result = _processor.Apply(model, _posts);
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            Assert.IsTrue(result.Count() == 2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        [TestMethod]
 | 
			
		||||
        public void CanSortBools()
 | 
			
		||||
        {
 | 
			
		||||
@@ -112,14 +111,12 @@ namespace SieveUnitTests
 | 
			
		||||
                Filters = "LikeCount==50",
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            Console.WriteLine(model.FiltersParsed.First().Value);
 | 
			
		||||
            Console.WriteLine(model.FiltersParsed.First().Operator);
 | 
			
		||||
            Console.WriteLine(model.FiltersParsed.First().OperatorParsed);
 | 
			
		||||
            Console.WriteLine(model.FiltersParsed[0].Value);
 | 
			
		||||
            Console.WriteLine(model.FiltersParsed[0].Operator);
 | 
			
		||||
            Console.WriteLine(model.FiltersParsed[0].OperatorParsed);
 | 
			
		||||
 | 
			
		||||
            var result = _processor.Apply(model, _posts);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            Assert.AreEqual(result.First().Id, 1);
 | 
			
		||||
            Assert.IsTrue(result.Count() == 1);
 | 
			
		||||
        }
 | 
			
		||||
@@ -169,15 +166,12 @@ namespace SieveUnitTests
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var result = _processor.Apply(model, _posts);
 | 
			
		||||
            var entry = result.FirstOrDefault();
 | 
			
		||||
            var resultCount = result.Count();
 | 
			
		||||
 | 
			
		||||
            Assert.AreEqual(result.First().Id, 3);
 | 
			
		||||
            Assert.IsTrue(result.Count() == 1);
 | 
			
		||||
            Assert.IsNotNull(entry);
 | 
			
		||||
            Assert.AreEqual(1, resultCount);
 | 
			
		||||
            Assert.AreEqual(3, entry.Id);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
//Sorts = "LikeCount",
 | 
			
		||||
//Page = 1,
 | 
			
		||||
//PageSize = 10
 | 
			
		||||
//
 | 
			
		||||
}
 | 
			
		||||
@@ -1,20 +1,18 @@
 | 
			
		||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using SieveUnitTests.Entities;
 | 
			
		||||
using SieveUnitTests.Services;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Sieve.Exceptions;
 | 
			
		||||
 | 
			
		||||
namespace SieveUnitTests
 | 
			
		||||
{
 | 
			
		||||
    [TestClass]
 | 
			
		||||
	[TestClass]
 | 
			
		||||
    public class Mapper
 | 
			
		||||
    {
 | 
			
		||||
        private ApplicationSieveProcessor _processor;
 | 
			
		||||
        private IQueryable<Post> _posts;
 | 
			
		||||
        private readonly ApplicationSieveProcessor _processor;
 | 
			
		||||
        private readonly IQueryable<Post> _posts;
 | 
			
		||||
 | 
			
		||||
        public Mapper()
 | 
			
		||||
        {
 | 
			
		||||
@@ -44,7 +42,7 @@ namespace SieveUnitTests
 | 
			
		||||
                },
 | 
			
		||||
            }.AsQueryable();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        [TestMethod]
 | 
			
		||||
        public void MapperWorks()
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,19 +2,15 @@
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using SieveUnitTests.Entities;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace SieveUnitTests.Services
 | 
			
		||||
{
 | 
			
		||||
    public class ApplicationSieveProcessor : SieveProcessor
 | 
			
		||||
	public class ApplicationSieveProcessor : SieveProcessor
 | 
			
		||||
    {
 | 
			
		||||
        public ApplicationSieveProcessor(
 | 
			
		||||
            IOptions<SieveOptions> options, 
 | 
			
		||||
            ISieveCustomSortMethods customSortMethods, 
 | 
			
		||||
            ISieveCustomFilterMethods customFilterMethods) 
 | 
			
		||||
            IOptions<SieveOptions> options,
 | 
			
		||||
            ISieveCustomSortMethods customSortMethods,
 | 
			
		||||
            ISieveCustomFilterMethods customFilterMethods)
 | 
			
		||||
            : base(options, customSortMethods, customFilterMethods)
 | 
			
		||||
        {
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +1,15 @@
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using SieveUnitTests.Entities;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace SieveUnitTests.Services
 | 
			
		||||
{
 | 
			
		||||
    public class SieveCustomFilterMethods : ISieveCustomFilterMethods
 | 
			
		||||
    {
 | 
			
		||||
        public IQueryable<Post> IsNew(IQueryable<Post> source, string op, string value)
 | 
			
		||||
        {
 | 
			
		||||
            var result = source.Where(p => p.LikeCount < 100);
 | 
			
		||||
        public IQueryable<Post> IsNew(IQueryable<Post> source)
 | 
			
		||||
            => source.Where(p => p.LikeCount < 100);
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public IQueryable<Comment> TestComment(IQueryable<Comment> source, string op, string value)
 | 
			
		||||
        {
 | 
			
		||||
            return source;
 | 
			
		||||
        }
 | 
			
		||||
        public IQueryable<Comment> TestComment(IQueryable<Comment> source)
 | 
			
		||||
            => source;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,15 @@
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Sieve.Services;
 | 
			
		||||
using SieveUnitTests.Entities;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace SieveUnitTests.Services
 | 
			
		||||
{
 | 
			
		||||
    public class SieveCustomSortMethods : ISieveCustomSortMethods
 | 
			
		||||
    {
 | 
			
		||||
        public IQueryable<Post> Popularity(IQueryable<Post> source, bool useThenBy, bool desc)
 | 
			
		||||
        {
 | 
			
		||||
            var result = useThenBy ?
 | 
			
		||||
                ((IOrderedQueryable<Post>)source).ThenBy(p => p.LikeCount) :
 | 
			
		||||
                source.OrderBy(p => p.LikeCount)
 | 
			
		||||
        public IQueryable<Post> Popularity(IQueryable<Post> source, bool useThenBy) => useThenBy
 | 
			
		||||
            ? ((IOrderedQueryable<Post>)source).ThenBy(p => p.LikeCount)
 | 
			
		||||
            : source.OrderBy(p => p.LikeCount)
 | 
			
		||||
                .ThenBy(p => p.CommentCount)
 | 
			
		||||
                .ThenBy(p => p.DateCreated);
 | 
			
		||||
 | 
			
		||||
            return result;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,15 @@
 | 
			
		||||
using Microsoft.Extensions.Options;
 | 
			
		||||
using Sieve.Models;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace SieveUnitTests
 | 
			
		||||
{
 | 
			
		||||
    public class SieveOptionsAccessor : IOptions<SieveOptions>
 | 
			
		||||
    {
 | 
			
		||||
        private SieveOptions _value;
 | 
			
		||||
 | 
			
		||||
        public SieveOptions Value
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return _value;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public SieveOptions Value { get; }
 | 
			
		||||
 | 
			
		||||
        public SieveOptionsAccessor()
 | 
			
		||||
        {
 | 
			
		||||
            _value = new SieveOptions()
 | 
			
		||||
            Value = new SieveOptions()
 | 
			
		||||
            {
 | 
			
		||||
                ThrowExceptions = true
 | 
			
		||||
            };
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user