Added support for generic filter and sort methods

This commit is contained in:
Steffen Kolmer 2019-03-24 19:45:23 +01:00
parent a582c6be06
commit e1bb069253
8 changed files with 94 additions and 11 deletions

View File

@ -84,6 +84,15 @@ public class SieveCustomSortMethods : ISieveCustomSortMethods
return result; // Must return modified IQueryable<TEntity> return result; // Must return modified IQueryable<TEntity>
} }
public IQueryable<T> Oldest<T>(IQueryable<T> source, bool useThenBy, bool desc) where T : BaseEntity // Generic functions are allowed too
{
var result = useThenBy ?
((IOrderedQueryable<T>)source).ThenByDescending(p => p.DateCreated) :
source.OrderByDescending(p => p.DateCreated);
return result;
}
} }
``` ```
And `SieveCustomFilterMethods`: And `SieveCustomFilterMethods`:
@ -97,6 +106,12 @@ public class SieveCustomFilterMethods : ISieveCustomFilterMethods
return result; // Must return modified IQueryable<TEntity> return result; // Must return modified IQueryable<TEntity>
} }
public IQueryable<T> Latest<T>(IQueryable<T> source, string op, string[] values) where T : BaseEntity // Generic functions are allowed too
{
var result = source.Where(c => c.DateCreated > DateTimeOffset.UtcNow.AddDays(-14));
return result;
}
} }
``` ```

View File

@ -387,6 +387,28 @@ namespace Sieve.Services
_options.Value.CaseSensitive ? BindingFlags.Default : BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance, _options.Value.CaseSensitive ? BindingFlags.Default : BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance,
typeof(IQueryable<TEntity>)); typeof(IQueryable<TEntity>));
if (customMethod == null)
{
// Find generic methods `public IQueryable<T> Filter<T>(IQueryable<T> source, ...)`
var genericCustomMethod = parent?.GetType()
.GetMethodExt(name,
_options.Value.CaseSensitive ? BindingFlags.Default : BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance,
typeof(IQueryable<>));
if (genericCustomMethod != null &&
genericCustomMethod.ReturnType.IsGenericType &&
genericCustomMethod.ReturnType.GetGenericTypeDefinition() == typeof(IQueryable<>))
{
var genericBaseType = genericCustomMethod.ReturnType.GenericTypeArguments[0];
var constraints = genericBaseType.GetGenericParameterConstraints();
if (constraints == null || constraints.Length == 0 || constraints.All((t) => t.IsAssignableFrom(typeof(TEntity))))
{
customMethod = genericCustomMethod.MakeGenericMethod(typeof(TEntity));
}
}
}
if (customMethod != null) if (customMethod != null)
{ {
try try

View File

@ -0,0 +1,13 @@
using System;
using Sieve.Attributes;
namespace SieveUnitTests.Entities
{
public class BaseEntity
{
public int Id { get; set; }
[Sieve(CanFilter = true, CanSort = true)]
public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.UtcNow;
}
}

View File

@ -3,13 +3,8 @@ using Sieve.Attributes;
namespace SieveUnitTests.Entities namespace SieveUnitTests.Entities
{ {
public class Comment public class Comment : BaseEntity
{ {
public int Id { get; set; }
[Sieve(CanFilter = true, CanSort = true)]
public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.UtcNow;
[Sieve(CanFilter = true)] [Sieve(CanFilter = true)]
public string Text { get; set; } public string Text { get; set; }
} }

View File

@ -3,9 +3,8 @@ using Sieve.Attributes;
namespace SieveUnitTests.Entities namespace SieveUnitTests.Entities
{ {
public class Post public class Post : BaseEntity
{ {
public int Id { get; set; }
[Sieve(CanFilter = true, CanSort = true)] [Sieve(CanFilter = true, CanSort = true)]
public string Title { get; set; } = Guid.NewGuid().ToString().Replace("-", string.Empty).Substring(0, 8); public string Title { get; set; } = Guid.NewGuid().ToString().Replace("-", string.Empty).Substring(0, 8);
@ -16,9 +15,6 @@ namespace SieveUnitTests.Entities
[Sieve(CanFilter = true, CanSort = true)] [Sieve(CanFilter = true, CanSort = true)]
public int CommentCount { get; set; } = new Random().Next(0, 1000); public int CommentCount { get; set; } = new Random().Next(0, 1000);
[Sieve(CanFilter = true, CanSort = true)]
public DateTimeOffset DateCreated { get; set; } = DateTimeOffset.UtcNow;
[Sieve(CanFilter = true, CanSort = true)] [Sieve(CanFilter = true, CanSort = true)]
public int? CategoryId { get; set; } = new Random().Next(0, 4); public int? CategoryId { get; set; } = new Random().Next(0, 4);

View File

@ -193,6 +193,20 @@ namespace SieveUnitTests
Assert.IsTrue(result.Count() == 3); Assert.IsTrue(result.Count() == 3);
} }
[TestMethod]
public void CustomGenericFiltersWork()
{
var model = new SieveModel()
{
Filters = "Latest",
};
var result = _processor.Apply(model, _comments);
Assert.IsFalse(result.Any(p => p.Id == 0));
Assert.IsTrue(result.Count() == 2);
}
[TestMethod] [TestMethod]
public void CustomFiltersWithOperatorsWork() public void CustomFiltersWithOperatorsWork()
{ {
@ -272,6 +286,19 @@ namespace SieveUnitTests
Assert.IsFalse(result.First().Id == 0); Assert.IsFalse(result.First().Id == 0);
} }
[TestMethod]
public void CustomGenericSortsWork()
{
var model = new SieveModel()
{
Sorts = "Oldest",
};
var result = _processor.Apply(model, _posts);
Assert.IsTrue(result.Last().Id == 0);
}
[TestMethod] [TestMethod]
public void MethodNotFoundExceptionWork() public void MethodNotFoundExceptionWork()
{ {

View File

@ -32,5 +32,11 @@ namespace SieveUnitTests.Services
{ {
return source; return source;
} }
public IQueryable<T> Latest<T>(IQueryable<T> source, string op, string[] values) where T : BaseEntity
{
var result = source.Where(c => c.DateCreated > DateTimeOffset.UtcNow.AddDays(-14));
return result;
}
} }
} }

View File

@ -16,5 +16,14 @@ namespace SieveUnitTests.Services
return result; return result;
} }
public IQueryable<T> Oldest<T>(IQueryable<T> source, bool useThenBy, bool desc) where T : BaseEntity
{
var result = useThenBy ?
((IOrderedQueryable<T>)source).ThenByDescending(p => p.DateCreated) :
source.OrderByDescending(p => p.DateCreated);
return result;
}
} }
} }