Merge pull request #60 from skolmer/generics

Added support for generic filter and sort methods
This commit is contained in:
Biarity 2019-11-16 10:55:18 +10:00 committed by GitHub
commit c04d94d22a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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>
}
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`:
@ -97,6 +106,12 @@ public class SieveCustomFilterMethods : ISieveCustomFilterMethods
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

@ -389,6 +389,28 @@ namespace Sieve.Services
_options.Value.CaseSensitive ? BindingFlags.Default : BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance,
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)
{
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
{
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)]
public string Text { get; set; }
}

View File

@ -3,9 +3,8 @@ using Sieve.Attributes;
namespace SieveUnitTests.Entities
{
public class Post
public class Post : BaseEntity
{
public int Id { get; set; }
[Sieve(CanFilter = true, CanSort = true)]
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)]
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)]
public int? CategoryId { get; set; } = new Random().Next(0, 4);

View File

@ -207,6 +207,20 @@ namespace SieveUnitTests
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]
public void CustomFiltersWithOperatorsWork()
{
@ -286,6 +300,19 @@ namespace SieveUnitTests
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]
public void MethodNotFoundExceptionWork()
{

View File

@ -32,5 +32,11 @@ namespace SieveUnitTests.Services
{
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;
}
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;
}
}
}