Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filtering is not working on Nullable properties #5

Open
j2lab opened this issue Aug 25, 2022 · 5 comments
Open

Filtering is not working on Nullable properties #5

j2lab opened this issue Aug 25, 2022 · 5 comments

Comments

@j2lab
Copy link

j2lab commented Aug 25, 2022

Steps to reproduce.

  1. Create a model that has a nullable property.
  2. Use the nullable property for filtering collection

Actual Result

System.InvalidOperationException : The binary operator Equal is not defined for the types 'System.Nullable`1[System.Int32]' and 'System.Int32'.

Stack Trace: 
Expression.GetEqualityComparisonOperator(ExpressionType binaryType, String opName, Expression left, Expression right, Boolean liftToNull)
Expression.Equal(Expression left, Expression right, Boolean liftToNull, MethodInfo method)
Expression.Equal(Expression left, Expression right)
DynamicExpressions.RobustEquals(MemberExpression prop, ConstantExpression constant) line 89
DynamicExpressions.CreateFilter(MemberExpression prop, FilterOperator op, ConstantExpression constant) line 62
DynamicExpressions.GetFilter(ParameterExpression param, String property, FilterOperator op, Object value) line 55
DynamicExpressions.GetPredicate[TEntity](String property, FilterOperator op, Object value) line 37
PredicateTests.GetPredicate_ShouldHandleNumericalOperators(Int32 id, String title, String property, FilterOperator op, Object value) line 22

@j2lab
Copy link
Author

j2lab commented Aug 26, 2022

Here is a fix for the nullable filter. Please review it

 private static Expression CreateFilter(MemberExpression prop, FilterOperator op, ConstantExpression constant)
        {
            if (prop.Type.Name.StartsWith("Nullable"))
            {
                var hasValueExpression = Expression.Property(prop, "HasValue");
                var valueExpression = Expression.Property(prop, "Value");

                var comparisonExpression = GenerateExpression(valueExpression, op, constant);

                return Expression.AndAlso(hasValueExpression, comparisonExpression);
            }
            else
                return GenerateExpression(prop, op, constant);
        }

        private static Expression GenerateExpression(MemberExpression prop, FilterOperator op, ConstantExpression constant)
        {
            return op switch
            {
                FilterOperator.Equals => RobustEquals(prop, constant),
                FilterOperator.GreaterThan => Expression.GreaterThan(prop, constant),
                FilterOperator.LessThan => Expression.LessThan(prop, constant),
                FilterOperator.ContainsIgnoreCase => Expression.Call(prop, _stringContainsMethodIgnoreCase, PrepareConstant(constant), Expression.Constant(StringComparison.OrdinalIgnoreCase)),
                FilterOperator.Contains => GetContainsMethodCallExpression(prop, constant),
                FilterOperator.NotContains => Expression.Not(GetContainsMethodCallExpression(prop, constant)),
                FilterOperator.ContainsKey => Expression.Call(prop, _dictionaryContainsKeyMethod, PrepareConstant(constant)),
                FilterOperator.NotContainsKey => Expression.Not(Expression.Call(prop, _dictionaryContainsKeyMethod, PrepareConstant(constant))),
                FilterOperator.ContainsValue => Expression.Call(prop, _dictionaryContainsValueMethod, PrepareConstant(constant)),
                FilterOperator.NotContainsValue => Expression.Not(Expression.Call(prop, _dictionaryContainsValueMethod, PrepareConstant(constant))),
                FilterOperator.StartsWith => Expression.Call(prop, _startsWithMethod, PrepareConstant(constant)),
                FilterOperator.EndsWith => Expression.Call(prop, _endsWithMethod, PrepareConstant(constant)),
                FilterOperator.DoesntEqual => Expression.Not(RobustEquals(prop, constant)),
                FilterOperator.GreaterThanOrEqual => Expression.GreaterThanOrEqual(prop, constant),
                FilterOperator.LessThanOrEqual => Expression.LessThanOrEqual(prop, constant),
                FilterOperator.IsEmpty => Expression.Call(_isNullOrEmtpyMethod, prop),
                FilterOperator.IsNotEmpty => Expression.Not(Expression.Call(_isNullOrEmtpyMethod, prop)),
                _ => throw new NotImplementedException()
            };
        }

@grgoncal
Copy link

Can someone review this?

@rene-mandel
Copy link

This needs attention.

@shawn-peery
Copy link

shawn-peery commented Dec 5, 2023

Just a mention here, it seems that this issue only occurs for Int32s. as when I try the same code with String's it doesn't have that issue.

Also looks like the issue happens on enum types as well

@zHaytam
Copy link
Owner

zHaytam commented Dec 5, 2023

I don't know why I was not receiving notifications for this repo...
I will look into this issue as soon as I can, sorry!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants