Skip to content

Commit

Permalink
v2.0.0-beta.2 (#16)
Browse files Browse the repository at this point in the history
* ignore possible null warning

* add support nested filtering for non-collection properties
fix null errors when any of nested collections are null

* ready to release beta2

* ignore nullable warning errors in test
  • Loading branch information
alirezanet authored Sep 16, 2021
1 parent 66fcd2a commit 4dff54d
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 36 deletions.
2 changes: 1 addition & 1 deletion src/Gridify.EntityFramework/Gridify.EntityFramework.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Gridify.EntityFramework</PackageId>
<Version>2.0.0-beta.1</Version>
<Version>2.0.0-beta.2</Version>
<Authors>Alireza Sabouri</Authors>
<Company>TuxTeam</Company>
<PackageDescription>Gridify (EntityFramework), Easy and optimized way to apply Filtering, Sorting, and Pagination using text-based data.</PackageDescription>
Expand Down
2 changes: 1 addition & 1 deletion src/Gridify/Gridify.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageId>Gridify</PackageId>
<Version>2.0.0-beta.1</Version>
<Version>2.0.0-beta.2</Version>
<Authors>Alireza Sabouri</Authors>
<Company>TuxTeam</Company>
<PackageDescription>Gridify, Easy and optimized way to apply Filtering, Sorting, and Pagination using text-based data.</PackageDescription>
Expand Down
4 changes: 2 additions & 2 deletions src/Gridify/PredicateBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> e
}


private class ReplaceExpressionVisitor : ExpressionVisitor
internal class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
Expand All @@ -44,7 +44,7 @@ public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)

public override Expression Visit(Expression node)
{
return node == _oldValue ? _newValue : base.Visit(node);
return node == _oldValue ? _newValue : base.Visit(node)!;
}
}

Expand Down
64 changes: 40 additions & 24 deletions src/Gridify/Syntax/SyntaxTreeToQueryConvertor.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand All @@ -10,8 +12,6 @@ public static class ExpressionToQueryConvertor
{
private static Expression<Func<T, bool>>? ConvertBinaryExpressionSyntaxToQuery<T>(BinaryExpressionSyntax binarySyntax, IGridifyMapper<T> mapper)
{
try
{
var left = (binarySyntax.Left as FieldExpressionSyntax)?.FieldToken.Text.Trim();
var right = (binarySyntax.Right as ValueExpressionSyntax)?.ValueToken.Text;
var op = binarySyntax.OperatorToken;
Expand All @@ -25,15 +25,8 @@ public static class ExpressionToQueryConvertor
if (gMap.IsNestedCollection)
return GenerateNestedExpression(mapper, gMap, right, op);

return GenerateExpression( gMap.To.Body, gMap.To.Parameters[0], right,
op,mapper.Configuration.AllowNullSearch, gMap.Convertor) as Expression<Func<T, bool>>;
}
catch (Exception)
{
// Unhandled exceptions ignores gridify completely,
// Not sure this is the best approach or not yet
return null;
}
return GenerateExpression(gMap.To.Body, gMap.To.Parameters[0], right,
op, mapper.Configuration.AllowNullSearch, gMap.Convertor) as Expression<Func<T, bool>>;
}

private static Expression<Func<T, bool>>? GenerateNestedExpression<T>(
Expand All @@ -43,15 +36,16 @@ public static class ExpressionToQueryConvertor
SyntaxNode op)
{
var body = gMap.To.Body;

if (body is MethodCallExpression selectExp && selectExp.Method.Name == "Select")
{
var targetExp = selectExp.Arguments.Single(a => a.NodeType == ExpressionType.Lambda) as LambdaExpression;
var conditionExp = GenerateExpression( targetExp!.Body, targetExp.Parameters[0], stringValue, op, mapper.Configuration.AllowNullSearch, gMap.Convertor);

var conditionExp = GenerateExpression(targetExp!.Body, targetExp.Parameters[0], stringValue, op, mapper.Configuration.AllowNullSearch,
gMap.Convertor);

if (conditionExp == null) return null;
return ParseMethodCallExpression(selectExp, conditionExp) as Expression<Func<T,bool>> ;

return ParseMethodCallExpression(selectExp, conditionExp) as Expression<Func<T, bool>>;
}

// this should never happening
Expand All @@ -63,26 +57,48 @@ private static LambdaExpression ParseMethodCallExpression(MethodCallExpression e
switch (exp.Arguments.First())
{
case MemberExpression member:
return GetAnyExpression(member,predicate);
case MethodCallExpression subExp when subExp.Arguments.Last() is LambdaExpression { Body: MemberExpression lambdaMember }:
return GetAnyExpression(member, predicate);
case MethodCallExpression subExp when subExp.Method.Name == "SelectMany" && subExp.Arguments.Last() is LambdaExpression { Body: MemberExpression lambdaMember }:
{
var newPredicate = GetAnyExpression(lambdaMember, predicate);
return ParseMethodCallExpression(subExp, newPredicate);
}
case MethodCallExpression subExp when subExp.Method.Name == "Select" && subExp.Arguments.Last() is LambdaExpression { Body: MemberExpression lambdaMember } lambda:
{
var newPredicate = GetAnyExpression(lambdaMember,predicate);
return ParseMethodCallExpression(subExp,newPredicate);
var newExp = new PredicateBuilder.ReplaceExpressionVisitor(predicate.Parameters[0], lambdaMember).Visit(predicate.Body);
var newPredicate = GetExpressionWithNullCheck(lambdaMember, lambda.Parameters[0], newExp!);
return ParseMethodCallExpression(subExp, newPredicate);
}
default:
throw new InvalidOperationException();
}
}

private static LambdaExpression GetAnyExpression(MemberExpression member, LambdaExpression predicate)
private static ParameterExpression GetParameterExpression(MemberExpression member)
{
var param = member.Expression as ParameterExpression;
return Expression.Parameter(member.Expression.Type, member.Expression.ToString());
}
private static LambdaExpression GetAnyExpression(MemberExpression member, Expression predicate)
{
var param = GetParameterExpression(member);
var prop = Expression.Property(param!, member.Member.Name);

var tp = prop.Type.GenericTypeArguments[0];
var anyMethod = GetAnyMethod(tp);
var newExp = Expression.Call(anyMethod, prop, predicate);
return Expression.Lambda(newExp, param);
var anyExp = Expression.Call(anyMethod, prop, predicate);

return GetExpressionWithNullCheck(prop, param, anyExp);

// return Expression.Lambda(anyExp, param);
}

private static LambdaExpression GetExpressionWithNullCheck(MemberExpression prop, ParameterExpression param, Expression right)
{
var nullChecker = Expression.NotEqual(prop, Expression.Constant(null));
var exp = Expression.AndAlso(nullChecker, right);
return Expression.Lambda(exp, param);
}

private static LambdaExpression? GenerateExpression(
Expression body,
ParameterExpression parameter,
Expand Down
51 changes: 43 additions & 8 deletions test/Gridify.Tests/GridifyNestedCollectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public class GridifyNestedCollectionTests

public GridifyNestedCollectionTests()
{
_fakeRepository3Nesting = new List<Level1>(GetSampleDataWith3Nestings());
_fakeRepository3Nesting = new List<Level1>(GetSampleDataWith3Nesting());
_fakeRepository2Nesting = new List<Level2>(GetSampleDataWith2Nestings());
}

Expand Down Expand Up @@ -52,12 +52,41 @@ public void Filtering_OnSecondLevelNestedProperty()
Assert.Equal(expected, actual);
Assert.True(actual.Any());
}
[Fact]
public void Filtering_OnThirdLevelNestedPropertyUsingSecondLevelProp()
{
var gm = new GridifyMapper<Level1>()
.GenerateMappings()
.AddMap("lvl", l1 => l1.Level2List.Select(l2 => l2.ChildProp).SelectMany(sl2 => sl2.Level3List).Select(l3=>l3.Level));


var actual = _fakeRepository3Nesting.AsQueryable()
.ApplyFiltering("lvl < 2", gm)
.ToList();

var expected = _fakeRepository3Nesting.Where(l1 => l1.Level2List != null && l1.Level2List.Any(l2 => l2.ChildProp != null && l2.ChildProp.Level3List != null &&
l2.ChildProp.Level3List.Any(l3 => l3.Level < 2))).ToList();

Assert.Equal(expected.Count, actual.Count);
Assert.Equal(expected, actual);
Assert.True(actual.Any());
}



#region TestData

private IEnumerable<Level1> GetSampleDataWith3Nestings()
private IEnumerable<Level1> GetSampleDataWith3Nesting()
{
var subLvl2 = new ChildProp()
{
Level3List = new List<Level3>() { new Level3() { Property1 = 2.0, Property2 = 100.0, Level = 1 }}
};
var subLvl2_2 = new ChildProp()
{
Level3List = new List<Level3>() { new Level3() { Property1 = 3.0, Property2 = 100.0, Level = 3 }}
};

yield return new Level1()
{
Id = 1,
Expand All @@ -66,11 +95,11 @@ private IEnumerable<Level1> GetSampleDataWith3Nestings()
{
new Level2()
{
Id = 101, Name = "Level2_1", Level3List = new List<Level3>() { new Level3() { Property1 = 2.0, Property2 = 100.0, Level = 0 } }
Id = 101, Name = "Level2_1", ChildProp = subLvl2 , Level3List = new List<Level3>() { new Level3() { Property1 = 2.0, Property2 = 100.0, Level = 0 } }
},
new Level2()
{
Id = 102, Name = "Level2_2", Level3List = new List<Level3>() { new Level3() { Property1 = 3.0, Property2 = 200.0, Level = 0 } }
Id = 102, Name = "Level2_2", ChildProp = new ChildProp() ,Level3List = new List<Level3>() { new Level3() { Property1 = 3.0, Property2 = 200.0, Level = 0 } }
},
new Level2()
{
Expand All @@ -90,11 +119,11 @@ private IEnumerable<Level1> GetSampleDataWith3Nestings()
},
new Level2()
{
Id = 102, Name = "Level2_2", Level3List = new List<Level3>() { new Level3() { Property1 = 5.0, Property2 = 200.0, Level = 0 } }
Id = 102, Name = "Level2_2", ChildProp = new ChildProp(), Level3List = new List<Level3>() { new Level3() { Property1 = 5.0, Property2 = 200.0, Level = 0 } }
},
new Level2()
{
Id = 103, Name = "Level2_3", Level3List = new List<Level3>() { new Level3() { Property1 = 6.0, Property2 = 300.0, Level = 0 } }
Id = 103, Name = "Level2_3", ChildProp = subLvl2_2 ,Level3List = new List<Level3>() { new Level3() { Property1 = 6.0, Property2 = 300.0, Level = 0 } }
}
}
};
Expand Down Expand Up @@ -145,7 +174,7 @@ private IEnumerable<Level2> GetSampleDataWith2Nestings()
}
};
}

#nullable disable
public class Level1
{
public int Id { get; set; }
Expand All @@ -158,8 +187,14 @@ public class Level2
public int Id { get; set; }
public string Name { get; set; }
public List<Level3> Level3List { get; set; }

public ChildProp ChildProp { get; set; }
}

public class ChildProp
{
public List<Level3> Level3List { get; set; }
}

public class Level3
{
public int Level { get; set; }
Expand Down

0 comments on commit 4dff54d

Please sign in to comment.