Skip to content

Commit

Permalink
Merge pull request #98 from hasanmanzak/master
Browse files Browse the repository at this point in the history
OrderByDynamic is modified to be able to handle inherited members...
  • Loading branch information
a-patel authored Dec 18, 2020
2 parents 51b5356 + a4509bb commit 8030550
Show file tree
Hide file tree
Showing 14 changed files with 776 additions and 18 deletions.
20 changes: 17 additions & 3 deletions Sieve/Extensions/OrderByDynamic.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Sieve.Extensions
{
Expand All @@ -9,10 +10,11 @@ public static partial class LinqExtensions
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")
Expand All @@ -28,15 +30,27 @@ public static IQueryable<TEntity> OrderByDynamic<TEntity>(
return source.Provider.CreateQuery<TEntity>(resultExpression);
}

private static Expression<Func<TEntity, object>> GenerateLambdaWithSafeMemberAccess<TEntity>(string fullPropertyName)
private static Expression<Func<TEntity, object>> GenerateLambdaWithSafeMemberAccess<TEntity>

This comment has been minimized.

Copy link
@joblo2021

joblo2021 Mar 14, 2021

Sieve/Extensions/OrderByDynamic.cs

(
string fullPropertyName,
PropertyInfo propertyInfo
)
{
var parameter = Expression.Parameter(typeof(TEntity), "e");
Expression propertyValue = parameter;
Expression nullCheck = null;

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())
{
Expand Down
40 changes: 30 additions & 10 deletions Sieve/Services/SieveProcessor.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
Expand Down Expand Up @@ -340,7 +341,7 @@ private IQueryable<TEntity> ApplySorting<TEntity>(

if (property != null)
{
result = result.OrderByDynamic(fullName, sortTerm.Descending, useThenBy);
result = result.OrderByDynamic(fullName, property, sortTerm.Descending, useThenBy);
}
else
{
Expand Down Expand Up @@ -461,16 +462,35 @@ private IQueryable<TEntity> ApplyCustomMethod<TEntity>(IQueryable<TEntity> resul
}
else
{
var incompatibleCustomMethod = parent?.GetType()
.GetMethod(name,
_options.Value.CaseSensitive ? BindingFlags.Default : BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);

if (incompatibleCustomMethod != null)
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 (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
{
Expand Down
10 changes: 10 additions & 0 deletions SieveUnitTests/Abstractions/Entity/IBaseEntity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace SieveUnitTests.Abstractions.Entity
{
public interface IBaseEntity
{
int Id { get; set; }
DateTimeOffset DateCreated { get; set; }
}
}
7 changes: 7 additions & 0 deletions SieveUnitTests/Abstractions/Entity/IComment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace SieveUnitTests.Abstractions.Entity
{
public interface IComment: IBaseEntity
{
string Text { get; set; }
}
}
24 changes: 24 additions & 0 deletions SieveUnitTests/Abstractions/Entity/IPost.cs
Original file line number Diff line number Diff line change
@@ -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; }
}
}
3 changes: 2 additions & 1 deletion SieveUnitTests/Entities/BaseEntity.cs
Original file line number Diff line number Diff line change
@@ -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; }

Expand Down
3 changes: 2 additions & 1 deletion SieveUnitTests/Entities/Comment.cs
Original file line number Diff line number Diff line change
@@ -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; }
Expand Down
3 changes: 2 additions & 1 deletion SieveUnitTests/Entities/Post.cs
Original file line number Diff line number Diff line change
@@ -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)]
Expand Down
19 changes: 19 additions & 0 deletions SieveUnitTests/General.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Sieve.Exceptions;
using Sieve.Models;
using Sieve.Services;
using SieveUnitTests.Abstractions.Entity;
using SieveUnitTests.Entities;
using SieveUnitTests.Services;

Expand Down Expand Up @@ -564,5 +565,23 @@ public void FilteringOnNullWorks()
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);
}
}
}
Loading

0 comments on commit 8030550

Please sign in to comment.