diff --git a/Sieve/Models/Allow.cs b/Sieve/Models/Allow.cs new file mode 100644 index 0000000..3f7bf64 --- /dev/null +++ b/Sieve/Models/Allow.cs @@ -0,0 +1,12 @@ +using System; + +namespace Sieve.Models +{ + [Flags] + public enum Allow + { + Sort = 1, + Filter = 2, + SortAndFilter = 4 + } +} \ No newline at end of file diff --git a/Sieve/Models/SieveProperty.cs b/Sieve/Models/SieveProperty.cs new file mode 100644 index 0000000..98bad2f --- /dev/null +++ b/Sieve/Models/SieveProperty.cs @@ -0,0 +1,43 @@ +using System; +using System.Linq.Expressions; +using System.Reflection; + +namespace Sieve.Models +{ + public class SieveProperty + { + public static SieveProperty For(Expression> expression, Allow allow, string nameInQuery = null) + { + var propertyInfo = GetPropertyInfo(expression); + + if (nameInQuery == null) + nameInQuery = propertyInfo.Name; + + return new SieveProperty(propertyInfo, nameInQuery, allow.HasFlag(Allow.Sort), allow.HasFlag(Allow.Filter)); + } + + private static PropertyInfo GetPropertyInfo(Expression> exp) + { + if (!(exp.Body is MemberExpression body)) + { + var ubody = (UnaryExpression) exp.Body; + body = ubody.Operand as MemberExpression; + } + + return body?.Member as PropertyInfo; + } + + public PropertyInfo PropertyInfo { get; } + public string NameInQuery { get; } + public bool CanSort { get; } + public bool CanFilter { get; } + + public SieveProperty(PropertyInfo propertyInfo, string nameInQuery, bool canSort, bool canFilter) + { + PropertyInfo = propertyInfo; + NameInQuery = nameInQuery; + CanSort = canSort; + CanFilter = canFilter; + } + } +} \ No newline at end of file diff --git a/Sieve/Services/ISieveProcessor.cs b/Sieve/Services/ISieveProcessor.cs index 6b0fa20..29c89a6 100644 --- a/Sieve/Services/ISieveProcessor.cs +++ b/Sieve/Services/ISieveProcessor.cs @@ -6,9 +6,9 @@ namespace Sieve.Services { public interface ISieveProcessor { - IQueryable ApplyAll(ISieveModel model, IQueryable source, object[] dataForCustomMethods = null); - IQueryable ApplySorting(ISieveModel model, IQueryable result, object[] dataForCustomMethods = null); - IQueryable ApplyFiltering(ISieveModel model, IQueryable result, object[] dataForCustomMethods = null); + IQueryable ApplyAll(ISieveModel model, IQueryable source, SieveProperty[] sieveProperties = null, object[] dataForCustomMethods = null); + IQueryable ApplySorting(ISieveModel model, IQueryable result, SieveProperty[] sieveProperties = null, object[] dataForCustomMethods = null); + IQueryable ApplyFiltering(ISieveModel model, IQueryable result, SieveProperty[] sieveProperties = null, object[] dataForCustomMethods = null); IQueryable ApplyPagination(ISieveModel model, IQueryable result); } } \ No newline at end of file diff --git a/Sieve/Services/SieveProcessor.cs b/Sieve/Services/SieveProcessor.cs index 8040002..6c0b0af 100644 --- a/Sieve/Services/SieveProcessor.cs +++ b/Sieve/Services/SieveProcessor.cs @@ -19,7 +19,6 @@ namespace Sieve.Services private IOptions _options; private ISieveCustomSortMethods _customSortMethods; private ISieveCustomFilterMethods _customFilterMethods; - public SieveProcessor(IOptions options, ISieveCustomSortMethods customSortMethods, @@ -49,7 +48,11 @@ namespace Sieve.Services _options = options; } - public IQueryable ApplyAll(ISieveModel model, IQueryable source, object[] dataForCustomMethods = null) + public IQueryable ApplyAll( + ISieveModel model, + IQueryable source, + SieveProperty[] sieveProperties = null, + object[] dataForCustomMethods = null) { var result = source; @@ -57,10 +60,10 @@ namespace Sieve.Services return result; // Filter - result = ApplyFiltering(model, result, dataForCustomMethods); + result = ApplyFiltering(model, result, sieveProperties, dataForCustomMethods); // Sort - result = ApplySorting(model, result, dataForCustomMethods); + result = ApplySorting(model, result, sieveProperties, dataForCustomMethods); // Paginate result = ApplyPagination(model, result); @@ -68,7 +71,11 @@ namespace Sieve.Services return result; } - public IQueryable ApplySorting(ISieveModel model, IQueryable result, object[] dataForCustomMethods = null) + public IQueryable ApplySorting( + ISieveModel model, + IQueryable result, + SieveProperty[] sieveProperties = null, + object[] dataForCustomMethods = null) { if (model?.SortsParsed == null) return result; @@ -76,7 +83,8 @@ namespace Sieve.Services var useThenBy = false; foreach (var sortTerm in model.SortsParsed) { - var property = GetSieveProperty(true, false, sortTerm.Name); + var property = sieveProperties?.FirstOrDefault(_ => _.NameInQuery == sortTerm.Name && _.CanSort)?.PropertyInfo + ?? GetSievePropertyViaAttribute(true, false, sortTerm.Name); if (property != null) { @@ -97,15 +105,20 @@ namespace Sieve.Services return result; } - - public IQueryable ApplyFiltering(ISieveModel model, IQueryable result, object[] dataForCustomMethods = null) + + public IQueryable ApplyFiltering( + ISieveModel model, + IQueryable result, + SieveProperty[] sieveProperties = null, + object[] dataForCustomMethods = null) { if (model?.FiltersParsed == null) return result; foreach (var filterTerm in model.FiltersParsed) { - var property = GetSieveProperty(false, true, filterTerm.Name); + var property = sieveProperties?.FirstOrDefault(_ => _.NameInQuery == filterTerm.Name && _.CanFilter)?.PropertyInfo + ?? GetSievePropertyViaAttribute(false, true, filterTerm.Name); if (property != null) { @@ -188,7 +201,7 @@ namespace Sieve.Services return result; } - private PropertyInfo GetSieveProperty(bool canSortRequired, bool canFilterRequired, string name) + private PropertyInfo GetSievePropertyViaAttribute(bool canSortRequired, bool canFilterRequired, string name) { return typeof(TEntity).GetProperties().FirstOrDefault(p => { diff --git a/SieveTests/Controllers/PostsController.cs b/SieveTests/Controllers/PostsController.cs index 92cf8fb..adb088e 100644 --- a/SieveTests/Controllers/PostsController.cs +++ b/SieveTests/Controllers/PostsController.cs @@ -33,6 +33,24 @@ namespace SieveTests.Controllers return Json(result.ToList()); } + [HttpGet] + public JsonResult GetAllWithSieveAndPropertyMapping(SieveModel sieveModel) + { + var result = _dbContext.Posts.AsNoTracking(); + + var sieveProperties = new[] + { + SieveProperty.For(_ => _.Title, Allow.Filter, "name"), + SieveProperty.For(_ => _.CommentCount, Allow.SortAndFilter), + SieveProperty.For(_ => _.LikeCount, Allow.Sort), + SieveProperty.For(_ => _.DateCreated, Allow.SortAndFilter), + }; + + result = _sieveProcessor.ApplyAll(sieveModel, result, sieveProperties); + + return Json(result.ToList()); + } + [HttpGet] public JsonResult Create(int number = 10) {