Base & test solution

This commit is contained in:
Biarity
2018-01-27 09:26:37 +10:00
parent 8407f9e23e
commit 37a6f9f70d
20 changed files with 543 additions and 8 deletions

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sieve.Attributes
{
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
class SieveAttribute : Attribute
{
/// <summary>
/// Override name used
/// </summary>
public string Name { get; set; }
public bool CanSort { get; set; }
public bool CanFilter { get; set; }
}
}

View File

@@ -1,8 +0,0 @@
using System;
namespace Sieve
{
public class Class1
{
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
namespace Sieve.Extensions
{
public static class LinqExtensions
{
public static IOrderedEnumerable<TSource> OrderByWithDirection<TSource, TKey>
(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
bool descending)
{
return descending ? source.OrderByDescending(keySelector)
: source.OrderBy(keySelector);
}
public static IOrderedQueryable<TSource> OrderByWithDirection<TSource, TKey>
(this IQueryable<TSource> source,
Expression<Func<TSource, TKey>> keySelector,
bool descending)
{
return descending ? source.OrderByDescending(keySelector)
: source.OrderBy(keySelector);
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sieve.Models
{
public enum FilterOperator
{
Equals,
GreaterThan,
LessThan,
GreaterThanOrEqualTo,
LessThanOrEqualTo,
Contains,
StartsWith
}
}

View File

@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Sieve.Models
{
public class FilterTerm
{
public string Name { get; set; }
public string Operator { get; set; }
[BindNever]
public FilterOperator OperatorParsed {
get {
switch (Operator.Trim().ToLower())
{
case "equals":
case "eq":
case "==":
return FilterOperator.Equals;
case "lessthan":
case "lt":
case "<":
return FilterOperator.LessThan;
case "greaterthan":
case "gt":
case ">":
return FilterOperator.GreaterThan;
case "greaterthanorequalto":
case "gte":
case ">=":
return FilterOperator.GreaterThanOrEqualTo;
case "lessthanorequalto":
case "lte":
case "<=":
return FilterOperator.LessThanOrEqualTo;
case "contains":
case "co":
case "@=":
return FilterOperator.Contains;
case "startswith":
case "sw":
case "_=":
return FilterOperator.StartsWith;
default:
return FilterOperator.Equals;
}
}
}
public string Value { get; set; }
public bool Descending { get; set; } = false;
}
}

View File

@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Sieve.Models
{
public class SieveModel
{
public IEnumerable<FilterTerm> Filter { get; set; }
public IEnumerable<SortTerm> Sort { get; set; }
[Range(1, Double.MaxValue)]
public int Page { get; set; } = 1;
[Range(1, Double.MaxValue)]
public int PageSize { get; set; } = 10;
}
}

View File

@@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sieve.Models
{
public class SieveOptions
{
}
}

14
Sieve/Models/SortTerm.cs Normal file
View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
namespace Sieve.Models
{
public class SortTerm
{
public string Name { get; set; }
public bool Descending { get; set; } = false;
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sieve.Services
{
public interface ISieveCustomFilterMethods
{
}
public interface ISieveCustomFilterMethods<TEntity>
where TEntity : class
{
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sieve.Services
{
public interface ISieveCustomSortMethods
{
}
public interface ISieveCustomSortMethods<TEntity>
where TEntity : class
{
}
}

View File

@@ -0,0 +1,151 @@
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.Data.Entity;
using System.Reflection;
using Sieve.Attributes;
using Sieve.Extensions;
namespace Sieve.Services
{
public class SieveProcessor<TEntity>
where TEntity: class
{
private IOptions<SieveOptions> _options;
private ISieveCustomSortMethods<TEntity> _customSortMethods;
private ISieveCustomFilterMethods<TEntity> _customFilterMethods;
public SieveProcessor(IOptions<SieveOptions> options,
ISieveCustomSortMethods<TEntity> customSortMethods,
ISieveCustomFilterMethods<TEntity> customFilterMethods)
{
_options = options;
_customSortMethods = customSortMethods;
_customFilterMethods = customFilterMethods;
}
public IEnumerable<TEntity> ApplyAll(SieveModel model, IQueryable<TEntity> source)
{
var result = source.AsNoTracking();
// Sort
result = ApplySort(model, result);
// Filter
result = ApplyFilter(model, result);
// Paginate
result = ApplyPagination(model, result);
return result;
}
public IQueryable<TEntity> ApplySort(SieveModel model, IQueryable<TEntity> result)
{
foreach (var sortTerm in model.Sort)
{
var property = GetSieveProperty(true, false, sortTerm.Name);
if (property != null)
{
result = result.OrderByWithDirection(
e => property.GetValue(e),
sortTerm.Descending);
}
else
{
var customMethod = _customSortMethods.GetType()
.GetMethod(sortTerm.Name);
if (customMethod != null)
{
result = result.OrderByWithDirection(
e => customMethod.Invoke(_customSortMethods, new object[] { e }),
sortTerm.Descending);
}
}
}
return result;
}
public IQueryable<TEntity> ApplyFilter(SieveModel model, IQueryable<TEntity> result)
{
foreach (var filterTerm in model.Filter)
{
var property = GetSieveProperty(false, true, filterTerm.Name);
if (property != null)
{
var filterValue = Convert.ChangeType(filterTerm.Value, property.GetType());
switch (filterTerm.OperatorParsed)
{
case FilterOperator.Equals:
result = result.Where(e => ((IComparable)property.GetValue(e)).Equals(filterValue));
break;
case FilterOperator.GreaterThan:
result = result.Where(e => ((IComparable)property.GetValue(e)).CompareTo(filterValue) > 0);
break;
case FilterOperator.LessThan:
result = result.Where(e => ((IComparable)property.GetValue(e)).CompareTo(filterValue) < 0);
break;
case FilterOperator.GreaterThanOrEqualTo:
result = result.Where(e => ((IComparable)property.GetValue(e)).CompareTo(filterValue) >= 0);
break;
case FilterOperator.LessThanOrEqualTo:
result = result.Where(e => ((IComparable)property.GetValue(e)).CompareTo(filterValue) <= 0);
break;
case FilterOperator.Contains:
result = result.Where(e => ((string)property.GetValue(e)).Contains((string)filterValue));
break;
case FilterOperator.StartsWith:
result = result.Where(e => ((string)property.GetValue(e)).StartsWith((string)filterValue));
break;
default:
result = result.Where(e => ((IComparable)property.GetValue(e)).Equals(filterValue));
break;
}
}
else
{
var customMethod = _customFilterMethods.GetType()
.GetMethod(filterTerm.Name);
if (customMethod != null)
{
result = result.Where(
e => (bool)customMethod.Invoke(_customFilterMethods, new object[] { e }));
}
}
}
return result;
}
public IQueryable<TEntity> ApplyPagination(SieveModel model, IQueryable<TEntity> result)
{
result = result.Skip((model.Page - 1) * model.PageSize)
.Take(model.PageSize);
return result;
}
private PropertyInfo GetSieveProperty(bool canSortRequired, bool canFilterRequired, string name)
{
return typeof(TEntity).GetProperties().FirstOrDefault(p =>
{
if (p.GetCustomAttribute(typeof(SieveAttribute)) is SieveAttribute sieveAttribute)
if ((canSortRequired ? sieveAttribute.CanSort : true) &&
(canFilterRequired ? sieveAttribute.CanFilter : true) &&
((sieveAttribute.Name ?? p.Name) == name))
return true;
return false;
});
}
}
}

View File

@@ -4,4 +4,10 @@
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="EntityFramework" Version="6.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.0.2" />
<PackageReference Include="Microsoft.Extensions.Options" Version="2.0.0" />
</ItemGroup>
</Project>