mirror of
https://github.com/Biarity/Sieve.git
synced 2024-11-21 21:12:50 +01:00
Merge pull request #98 from hasanmanzak/master
OrderByDynamic is modified to be able to handle inherited members...
This commit is contained in:
commit
803055029e
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Sieve.Extensions
|
||||
{
|
||||
@ -9,10 +10,11 @@ namespace Sieve.Extensions
|
||||
public static IQueryable<TEntity> OrderByDynamic<TEntity>(
|
||||
this IQueryable<TEntity> source,
|
||||
string fullPropertyName,
|
||||
PropertyInfo propertyInfo,
|
||||
bool desc,
|
||||
bool useThenBy)
|
||||
{
|
||||
var lambda = GenerateLambdaWithSafeMemberAccess<TEntity>(fullPropertyName);
|
||||
var lambda = GenerateLambdaWithSafeMemberAccess<TEntity>(fullPropertyName, propertyInfo);
|
||||
|
||||
var command = desc
|
||||
? (useThenBy ? "ThenByDescending" : "OrderByDescending")
|
||||
@ -28,7 +30,11 @@ namespace Sieve.Extensions
|
||||
return source.Provider.CreateQuery<TEntity>(resultExpression);
|
||||
}
|
||||
|
||||
private static Expression<Func<TEntity, object>> GenerateLambdaWithSafeMemberAccess<TEntity>(string fullPropertyName)
|
||||
private static Expression<Func<TEntity, object>> GenerateLambdaWithSafeMemberAccess<TEntity>
|
||||
(
|
||||
string fullPropertyName,
|
||||
PropertyInfo propertyInfo
|
||||
)
|
||||
{
|
||||
var parameter = Expression.Parameter(typeof(TEntity), "e");
|
||||
Expression propertyValue = parameter;
|
||||
@ -36,7 +42,15 @@ namespace Sieve.Extensions
|
||||
|
||||
foreach (var name in fullPropertyName.Split('.'))
|
||||
{
|
||||
propertyValue = Expression.PropertyOrField(propertyValue, name);
|
||||
try
|
||||
{
|
||||
propertyValue = Expression.PropertyOrField(propertyValue, name);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// name is not a direct property of field of propertyValue expression. construct a memberAccess then.
|
||||
propertyValue = Expression.MakeMemberAccess(propertyValue, propertyInfo);
|
||||
}
|
||||
|
||||
if (propertyValue.Type.IsNullable())
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
@ -340,7 +341,7 @@ namespace Sieve.Services
|
||||
|
||||
if (property != null)
|
||||
{
|
||||
result = result.OrderByDynamic(fullName, sortTerm.Descending, useThenBy);
|
||||
result = result.OrderByDynamic(fullName, property, sortTerm.Descending, useThenBy);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -461,16 +462,35 @@ namespace Sieve.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
var incompatibleCustomMethod = parent?.GetType()
|
||||
.GetMethod(name,
|
||||
_options.Value.CaseSensitive ? BindingFlags.Default : BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
|
||||
var incompatibleCustomMethods = parent?
|
||||
.GetType()
|
||||
.GetMethods
|
||||
(
|
||||
_options.Value.CaseSensitive
|
||||
? BindingFlags.Default
|
||||
: BindingFlags.IgnoreCase | BindingFlags.Public |
|
||||
BindingFlags.Instance
|
||||
)
|
||||
.Where(method => string.Equals(method.Name, name,
|
||||
_options.Value.CaseSensitive
|
||||
? StringComparison.InvariantCulture
|
||||
: StringComparison.InvariantCultureIgnoreCase))
|
||||
.ToList()
|
||||
??
|
||||
new List<MethodInfo>();
|
||||
|
||||
if (incompatibleCustomMethod != null)
|
||||
if (incompatibleCustomMethods.Any())
|
||||
{
|
||||
var expected = typeof(IQueryable<TEntity>);
|
||||
var actual = incompatibleCustomMethod.ReturnType;
|
||||
throw new SieveIncompatibleMethodException(name, expected, actual,
|
||||
$"{name} failed. Expected a custom method for type {expected} but only found for type {actual}");
|
||||
var incompatibles =
|
||||
from incompatibleCustomMethod in incompatibleCustomMethods
|
||||
let expected = typeof(IQueryable<TEntity>)
|
||||
let actual = incompatibleCustomMethod.ReturnType
|
||||
select new SieveIncompatibleMethodException(name, expected, actual,
|
||||
$"{name} failed. Expected a custom method for type {expected} but only found for type {actual}");
|
||||
|
||||
var aggregate = new AggregateException(incompatibles);
|
||||
|
||||
throw new SieveIncompatibleMethodException(aggregate.Message, aggregate);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
10
SieveUnitTests/Abstractions/Entity/IBaseEntity.cs
Normal file
10
SieveUnitTests/Abstractions/Entity/IBaseEntity.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace SieveUnitTests.Abstractions.Entity
|
||||
{
|
||||
public interface IBaseEntity
|
||||
{
|
||||
int Id { get; set; }
|
||||
DateTimeOffset DateCreated { get; set; }
|
||||
}
|
||||
}
|
7
SieveUnitTests/Abstractions/Entity/IComment.cs
Normal file
7
SieveUnitTests/Abstractions/Entity/IComment.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace SieveUnitTests.Abstractions.Entity
|
||||
{
|
||||
public interface IComment: IBaseEntity
|
||||
{
|
||||
string Text { get; set; }
|
||||
}
|
||||
}
|
24
SieveUnitTests/Abstractions/Entity/IPost.cs
Normal file
24
SieveUnitTests/Abstractions/Entity/IPost.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using Sieve.Attributes;
|
||||
using SieveUnitTests.Entities;
|
||||
|
||||
namespace SieveUnitTests.Abstractions.Entity
|
||||
{
|
||||
public interface IPost: IBaseEntity
|
||||
{
|
||||
[Sieve(CanFilter = true, CanSort = true)]
|
||||
string Title { get; set; }
|
||||
[Sieve(CanFilter = true, CanSort = true)]
|
||||
int LikeCount { get; set; }
|
||||
[Sieve(CanFilter = true, CanSort = true)]
|
||||
int CommentCount { get; set; }
|
||||
[Sieve(CanFilter = true, CanSort = true)]
|
||||
int? CategoryId { get; set; }
|
||||
[Sieve(CanFilter = true, CanSort = true)]
|
||||
bool IsDraft { get; set; }
|
||||
string ThisHasNoAttribute { get; set; }
|
||||
string ThisHasNoAttributeButIsAccessible { get; set; }
|
||||
int OnlySortableViaFluentApi { get; set; }
|
||||
Comment TopComment { get; set; }
|
||||
Comment FeaturedComment { get; set; }
|
||||
}
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using Sieve.Attributes;
|
||||
using SieveUnitTests.Abstractions.Entity;
|
||||
|
||||
namespace SieveUnitTests.Entities
|
||||
{
|
||||
public class BaseEntity
|
||||
public class BaseEntity : IBaseEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
using Sieve.Attributes;
|
||||
using SieveUnitTests.Abstractions.Entity;
|
||||
|
||||
namespace SieveUnitTests.Entities
|
||||
{
|
||||
public class Comment : BaseEntity
|
||||
public class Comment : BaseEntity, IComment
|
||||
{
|
||||
[Sieve(CanFilter = true)]
|
||||
public string Text { get; set; }
|
||||
|
@ -1,9 +1,10 @@
|
||||
using System;
|
||||
using Sieve.Attributes;
|
||||
using SieveUnitTests.Abstractions.Entity;
|
||||
|
||||
namespace SieveUnitTests.Entities
|
||||
{
|
||||
public class Post : BaseEntity
|
||||
public class Post : BaseEntity, IPost
|
||||
{
|
||||
|
||||
[Sieve(CanFilter = true, CanSort = true)]
|
||||
|
@ -5,6 +5,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Sieve.Exceptions;
|
||||
using Sieve.Models;
|
||||
using Sieve.Services;
|
||||
using SieveUnitTests.Abstractions.Entity;
|
||||
using SieveUnitTests.Entities;
|
||||
using SieveUnitTests.Services;
|
||||
|
||||
@ -564,5 +565,23 @@ namespace SieveUnitTests
|
||||
var filteredPosts = result.ToList();
|
||||
Assert.AreEqual(filteredPosts[0].Id, 2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BaseDefinedPropertyMappingSortingWorks_WithCustomName()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Sorts = "-CreateDate"
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
Assert.AreEqual(4, result.Count());
|
||||
|
||||
var posts = result.ToList();
|
||||
Assert.AreEqual(posts[0].Id, 3);
|
||||
Assert.AreEqual(posts[1].Id, 2);
|
||||
Assert.AreEqual(posts[2].Id, 1);
|
||||
Assert.AreEqual(posts[3].Id, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
588
SieveUnitTests/GeneralWithInterfaces.cs
Normal file
588
SieveUnitTests/GeneralWithInterfaces.cs
Normal file
@ -0,0 +1,588 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Sieve.Exceptions;
|
||||
using Sieve.Models;
|
||||
using Sieve.Services;
|
||||
using SieveUnitTests.Abstractions.Entity;
|
||||
using SieveUnitTests.Entities;
|
||||
using SieveUnitTests.Services;
|
||||
|
||||
namespace SieveUnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class GeneralWithInterfaces
|
||||
{
|
||||
private readonly SieveProcessor _processor;
|
||||
private readonly IQueryable<IPost> _posts;
|
||||
private readonly IQueryable<Comment> _comments;
|
||||
|
||||
public GeneralWithInterfaces()
|
||||
{
|
||||
_processor = new ApplicationSieveProcessor(new SieveOptionsAccessor(),
|
||||
new SieveCustomSortMethods(),
|
||||
new SieveCustomFilterMethods());
|
||||
|
||||
_posts = new List<IPost>
|
||||
{
|
||||
new Post() {
|
||||
Id = 0,
|
||||
Title = "A",
|
||||
LikeCount = 100,
|
||||
IsDraft = true,
|
||||
CategoryId = null,
|
||||
TopComment = new Comment { Id = 0, Text = "A1" },
|
||||
FeaturedComment = new Comment { Id = 4, Text = "A2" }
|
||||
},
|
||||
new Post() {
|
||||
Id = 1,
|
||||
Title = "B",
|
||||
LikeCount = 50,
|
||||
IsDraft = false,
|
||||
CategoryId = 1,
|
||||
TopComment = new Comment { Id = 3, Text = "B1" },
|
||||
FeaturedComment = new Comment { Id = 5, Text = "B2" }
|
||||
},
|
||||
new Post() {
|
||||
Id = 2,
|
||||
Title = "C",
|
||||
LikeCount = 0,
|
||||
CategoryId = 1,
|
||||
TopComment = new Comment { Id = 2, Text = "C1" },
|
||||
FeaturedComment = new Comment { Id = 6, Text = "C2" }
|
||||
},
|
||||
new Post() {
|
||||
Id = 3,
|
||||
Title = "D",
|
||||
LikeCount = 3,
|
||||
IsDraft = true,
|
||||
CategoryId = 2,
|
||||
TopComment = new Comment { Id = 1, Text = "D1" },
|
||||
FeaturedComment = new Comment { Id = 7, Text = "D2" }
|
||||
},
|
||||
}.AsQueryable();
|
||||
|
||||
_comments = new List<Comment>
|
||||
{
|
||||
new Comment() {
|
||||
Id = 0,
|
||||
DateCreated = DateTimeOffset.UtcNow.AddDays(-20),
|
||||
Text = "This is an old comment."
|
||||
},
|
||||
new Comment() {
|
||||
Id = 1,
|
||||
DateCreated = DateTimeOffset.UtcNow.AddDays(-1),
|
||||
Text = "This is a fairly new comment."
|
||||
},
|
||||
new Comment() {
|
||||
Id = 2,
|
||||
DateCreated = DateTimeOffset.UtcNow,
|
||||
Text = "This is a brand new comment. (Text in braces)"
|
||||
},
|
||||
}.AsQueryable();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ContainsCanBeCaseInsensitive()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "Title@=*a"
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.AreEqual(result.First().Id, 0);
|
||||
Assert.IsTrue(result.Count() == 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NotEqualsCanBeCaseInsensitive()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "Title!=*a"
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.AreEqual(result.First().Id, 1);
|
||||
Assert.IsTrue(result.Count() == 3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ContainsIsCaseSensitive()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "Title@=a",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.IsTrue(result.Count() == 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NotContainsWorks()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "Title!@=D",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.IsTrue(result.Count() == 3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanFilterBools()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "IsDraft==false"
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.IsTrue(result.Count() == 2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanSortBools()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Sorts = "-IsDraft"
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.AreEqual(result.First().Id, 0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CanFilterNullableInts()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "CategoryId==1"
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.IsTrue(result.Count() == 2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EqualsDoesntFailWithNonStringTypes()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "LikeCount==50",
|
||||
};
|
||||
|
||||
Console.WriteLine(model.GetFiltersParsed()[0].Values);
|
||||
Console.WriteLine(model.GetFiltersParsed()[0].Operator);
|
||||
Console.WriteLine(model.GetFiltersParsed()[0].OperatorParsed);
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.AreEqual(result.First().Id, 1);
|
||||
Assert.IsTrue(result.Count() == 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CustomFiltersWork()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "Isnew",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.IsFalse(result.Any(p => p.Id == 0));
|
||||
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()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "HasInTitle==A",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.IsTrue(result.Any(p => p.Id == 0));
|
||||
Assert.IsTrue(result.Count() == 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CustomFiltersMixedWithUsualWork1()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "Isnew,CategoryId==2",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.IsTrue(result.Any(p => p.Id == 3));
|
||||
Assert.IsTrue(result.Count() == 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CustomFiltersMixedWithUsualWork2()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "CategoryId==2,Isnew",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
Assert.IsTrue(result.Any(p => p.Id == 3));
|
||||
Assert.IsTrue(result.Count() == 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CustomFiltersOnDifferentSourcesCanShareName()
|
||||
{
|
||||
var postModel = new SieveModel()
|
||||
{
|
||||
Filters = "CategoryId==2,Isnew",
|
||||
};
|
||||
|
||||
var postResult = _processor.Apply(postModel, _posts);
|
||||
|
||||
Assert.IsTrue(postResult.Any(p => p.Id == 3));
|
||||
Assert.AreEqual(1, postResult.Count());
|
||||
|
||||
var commentModel = new SieveModel()
|
||||
{
|
||||
Filters = "Isnew",
|
||||
};
|
||||
|
||||
var commentResult = _processor.Apply(commentModel, _comments);
|
||||
|
||||
Assert.IsTrue(commentResult.Any(c => c.Id == 2));
|
||||
Assert.AreEqual(2, commentResult.Count());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CustomSortsWork()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Sorts = "Popularity",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
|
||||
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()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "does not exist",
|
||||
};
|
||||
|
||||
Assert.ThrowsException<SieveMethodNotFoundException>(() => _processor.Apply(model, _posts));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void IncompatibleMethodExceptionsWork()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "TestComment",
|
||||
};
|
||||
|
||||
Assert.ThrowsException<SieveIncompatibleMethodException>(() => _processor.Apply(model, _posts));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OrNameFilteringWorks()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "(Title|LikeCount)==3",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
var entry = result.FirstOrDefault();
|
||||
var resultCount = result.Count();
|
||||
|
||||
Assert.IsNotNull(entry);
|
||||
Assert.AreEqual(1, resultCount);
|
||||
Assert.AreEqual(3, entry.Id);
|
||||
}
|
||||
|
||||
[DataTestMethod]
|
||||
[DataRow("CategoryId==1,(CategoryId|LikeCount)==50")]
|
||||
[DataRow("(CategoryId|LikeCount)==50,CategoryId==1")]
|
||||
public void CombinedAndOrFilterIndependentOfOrder(string filter)
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = filter,
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
var entry = result.FirstOrDefault();
|
||||
var resultCount = result.Count();
|
||||
|
||||
Assert.IsNotNull(entry);
|
||||
Assert.AreEqual(1, resultCount);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void CombinedAndOrWithSpaceFilteringWorks()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "Title==D, (Title|LikeCount)==3",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
var entry = result.FirstOrDefault();
|
||||
var resultCount = result.Count();
|
||||
|
||||
Assert.IsNotNull(entry);
|
||||
Assert.AreEqual(1, resultCount);
|
||||
Assert.AreEqual(3, entry.Id);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OrValueFilteringWorks()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "Title==C|D",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
Assert.AreEqual(2, result.Count());
|
||||
Assert.IsTrue(result.Any(p => p.Id == 2));
|
||||
Assert.IsTrue(result.Any(p => p.Id == 3));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void OrValueFilteringWorks2()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "Text@=(|)",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _comments);
|
||||
Assert.AreEqual(1, result.Count());
|
||||
Assert.AreEqual(2, result.FirstOrDefault().Id);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NestedFilteringWorks()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "TopComment.Text!@=A",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
Assert.AreEqual(3, result.Count());
|
||||
var posts = result.ToList();
|
||||
Assert.IsTrue(posts[0].TopComment.Text.Contains("B"));
|
||||
Assert.IsTrue(posts[1].TopComment.Text.Contains("C"));
|
||||
Assert.IsTrue(posts[2].TopComment.Text.Contains("D"));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NestedSortingWorks()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Sorts = "TopComment.Id",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
Assert.AreEqual(4, result.Count());
|
||||
var posts = result.ToList();
|
||||
Assert.AreEqual(posts[0].Id, 0);
|
||||
Assert.AreEqual(posts[1].Id, 3);
|
||||
Assert.AreEqual(posts[2].Id, 2);
|
||||
Assert.AreEqual(posts[3].Id, 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void NestedFilteringWithIdenticTypesWorks()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "(topc|featc)@=*2",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
Assert.AreEqual(4, result.Count());
|
||||
|
||||
model = new SieveModel()
|
||||
{
|
||||
Filters = "(topc|featc)@=*B",
|
||||
};
|
||||
|
||||
result = _processor.Apply(model, _posts);
|
||||
Assert.AreEqual(1, result.Count());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FilteringNullsWorks()
|
||||
{
|
||||
var posts = new List<Post>
|
||||
{
|
||||
new Post() {
|
||||
Id = 1,
|
||||
Title = null,
|
||||
LikeCount = 0,
|
||||
IsDraft = false,
|
||||
CategoryId = null,
|
||||
TopComment = null,
|
||||
FeaturedComment = null
|
||||
},
|
||||
}.AsQueryable();
|
||||
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "FeaturedComment.Text!@=Some value",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, posts);
|
||||
Assert.AreEqual(0, result.Count());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SortingNullsWorks()
|
||||
{
|
||||
var posts = new List<Post>
|
||||
{
|
||||
new Post() {
|
||||
Id = 1,
|
||||
Title = null,
|
||||
LikeCount = 0,
|
||||
IsDraft = false,
|
||||
CategoryId = null,
|
||||
TopComment = new Comment { Id = 1 },
|
||||
FeaturedComment = null
|
||||
},
|
||||
new Post() {
|
||||
Id = 2,
|
||||
Title = null,
|
||||
LikeCount = 0,
|
||||
IsDraft = false,
|
||||
CategoryId = null,
|
||||
TopComment = null,
|
||||
FeaturedComment = null
|
||||
},
|
||||
}.AsQueryable();
|
||||
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Sorts = "TopComment.Id",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, posts);
|
||||
Assert.AreEqual(2, result.Count());
|
||||
var sortedPosts = result.ToList();
|
||||
Assert.AreEqual(sortedPosts[0].Id, 2);
|
||||
Assert.AreEqual(sortedPosts[1].Id, 1);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FilteringOnNullWorks()
|
||||
{
|
||||
var posts = new List<Post>
|
||||
{
|
||||
new Post() {
|
||||
Id = 1,
|
||||
Title = null,
|
||||
LikeCount = 0,
|
||||
IsDraft = false,
|
||||
CategoryId = null,
|
||||
TopComment = null,
|
||||
FeaturedComment = null
|
||||
},
|
||||
new Post() {
|
||||
Id = 2,
|
||||
Title = null,
|
||||
LikeCount = 0,
|
||||
IsDraft = false,
|
||||
CategoryId = null,
|
||||
TopComment = null,
|
||||
FeaturedComment = new Comment { Id = 1, Text = null }
|
||||
},
|
||||
}.AsQueryable();
|
||||
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Filters = "FeaturedComment.Text==null",
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, posts);
|
||||
Assert.AreEqual(1, result.Count());
|
||||
var filteredPosts = result.ToList();
|
||||
Assert.AreEqual(filteredPosts[0].Id, 2);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BaseDefinedPropertyMappingSortingWorks_WithCustomName()
|
||||
{
|
||||
var model = new SieveModel()
|
||||
{
|
||||
Sorts = "-CreateDate"
|
||||
};
|
||||
|
||||
var result = _processor.Apply(model, _posts);
|
||||
Assert.AreEqual(4, result.Count());
|
||||
|
||||
var posts = result.ToList();
|
||||
Assert.AreEqual(posts[0].Id, 3);
|
||||
Assert.AreEqual(posts[1].Id, 2);
|
||||
Assert.AreEqual(posts[2].Id, 1);
|
||||
Assert.AreEqual(posts[3].Id, 0);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Sieve.Models;
|
||||
using Sieve.Services;
|
||||
using SieveUnitTests.Abstractions.Entity;
|
||||
using SieveUnitTests.Entities;
|
||||
|
||||
namespace SieveUnitTests.Services
|
||||
@ -39,6 +40,39 @@ namespace SieveUnitTests.Services
|
||||
.CanFilter()
|
||||
.HasName("featc");
|
||||
|
||||
mapper
|
||||
.Property<Post>(p => p.DateCreated)
|
||||
.CanSort()
|
||||
.HasName("CreateDate");
|
||||
|
||||
// interfaces
|
||||
mapper.Property<IPost>(p => p.ThisHasNoAttributeButIsAccessible)
|
||||
.CanSort()
|
||||
.CanFilter()
|
||||
.HasName("shortname");
|
||||
|
||||
mapper.Property<IPost>(p => p.TopComment.Text)
|
||||
.CanFilter();
|
||||
|
||||
mapper.Property<IPost>(p => p.TopComment.Id)
|
||||
.CanSort();
|
||||
|
||||
mapper.Property<IPost>(p => p.OnlySortableViaFluentApi)
|
||||
.CanSort();
|
||||
|
||||
mapper.Property<IPost>(p => p.TopComment.Text)
|
||||
.CanFilter()
|
||||
.HasName("topc");
|
||||
|
||||
mapper.Property<IPost>(p => p.FeaturedComment.Text)
|
||||
.CanFilter()
|
||||
.HasName("featc");
|
||||
|
||||
mapper
|
||||
.Property<IPost>(p => p.DateCreated)
|
||||
.CanSort()
|
||||
.HasName("CreateDate");
|
||||
|
||||
return mapper;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Sieve.Services;
|
||||
using SieveUnitTests.Abstractions.Entity;
|
||||
using SieveUnitTests.Entities;
|
||||
|
||||
namespace SieveUnitTests.Services
|
||||
@ -38,5 +39,31 @@ namespace SieveUnitTests.Services
|
||||
var result = source.Where(c => c.DateCreated > DateTimeOffset.UtcNow.AddDays(-14));
|
||||
return result;
|
||||
}
|
||||
|
||||
public IQueryable<IPost> IsNew(IQueryable<IPost> source, string op, string[] values)
|
||||
{
|
||||
var result = source.Where(p => p.LikeCount < 100);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IQueryable<IPost> HasInTitle(IQueryable<IPost> source, string op, string[] values)
|
||||
{
|
||||
var result = source.Where(p => p.Title.Contains(values[0]));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IQueryable<IComment> IsNew(IQueryable<IComment> source, string op, string[] values)
|
||||
{
|
||||
var result = source.Where(c => c.DateCreated > DateTimeOffset.UtcNow.AddDays(-2));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IQueryable<IComment> TestComment(IQueryable<IComment> source, string op, string[] values)
|
||||
{
|
||||
return source;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Linq;
|
||||
using Sieve.Services;
|
||||
using SieveUnitTests.Abstractions.Entity;
|
||||
using SieveUnitTests.Entities;
|
||||
|
||||
namespace SieveUnitTests.Services
|
||||
@ -17,7 +18,18 @@ namespace SieveUnitTests.Services
|
||||
return result;
|
||||
}
|
||||
|
||||
public IQueryable<T> Oldest<T>(IQueryable<T> source, bool useThenBy, bool desc) where T : BaseEntity
|
||||
public IQueryable<IPost> Popularity(IQueryable<IPost> source, bool useThenBy, bool desc)
|
||||
{
|
||||
var result = useThenBy ?
|
||||
((IOrderedQueryable<IPost>)source).ThenBy(p => p.LikeCount) :
|
||||
source.OrderBy(p => p.LikeCount)
|
||||
.ThenBy(p => p.CommentCount)
|
||||
.ThenBy(p => p.DateCreated);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IQueryable<T> Oldest<T>(IQueryable<T> source, bool useThenBy, bool desc) where T : IBaseEntity
|
||||
{
|
||||
var result = useThenBy ?
|
||||
((IOrderedQueryable<T>)source).ThenByDescending(p => p.DateCreated) :
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using Sieve.Models;
|
||||
|
||||
namespace SieveUnitTests
|
||||
namespace SieveUnitTests.Services
|
||||
{
|
||||
public class SieveOptionsAccessor : IOptions<SieveOptions>
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user