From 069b290a79d5864384cde5f2e47c9796116ea5fe Mon Sep 17 00:00:00 2001 From: khai Date: Thu, 6 Mar 2025 09:14:00 +0700 Subject: [PATCH 1/3] Fix nullable type --- DynamicExpressions.Benchmarks/Entry.cs | 2 +- DynamicExpressions/DynamicExpressions.cs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/DynamicExpressions.Benchmarks/Entry.cs b/DynamicExpressions.Benchmarks/Entry.cs index 918b5b5..11f3a6d 100644 --- a/DynamicExpressions.Benchmarks/Entry.cs +++ b/DynamicExpressions.Benchmarks/Entry.cs @@ -4,7 +4,7 @@ public class Entry { public string Title { get; set; } - public int Number { get; set; } + public int? Number { get; set; } public SubEntry SubEntry { get; set; } public Entry(string title, int number) diff --git a/DynamicExpressions/DynamicExpressions.cs b/DynamicExpressions/DynamicExpressions.cs index 692ff0a..449fa1e 100644 --- a/DynamicExpressions/DynamicExpressions.cs +++ b/DynamicExpressions/DynamicExpressions.cs @@ -86,9 +86,19 @@ private static Expression RobustEquals(MemberExpression prop, ConstantExpression { return Expression.Equal(prop, Expression.Constant(val)); } + if (IsNullableType(prop.Type)) + { + var constantNullable = Expression.Convert(constant, prop.Type); + return Expression.Equal(prop, constantNullable); + } return Expression.Equal(prop, constant); } + private static bool IsNullableType(Type t) + { + return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>); + } + private static Expression GetContainsMethodCallExpression(MemberExpression prop, ConstantExpression constant) { if (prop.Type == _stringType) From 722099aca5ca989e55eb10fccf52065867194468 Mon Sep 17 00:00:00 2001 From: khai Date: Thu, 6 Mar 2025 13:18:05 +0700 Subject: [PATCH 2/3] Add Nullable --- DynamicExpressions/DynamicExpressions.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/DynamicExpressions/DynamicExpressions.cs b/DynamicExpressions/DynamicExpressions.cs index 449fa1e..6e7ea5f 100644 --- a/DynamicExpressions/DynamicExpressions.cs +++ b/DynamicExpressions/DynamicExpressions.cs @@ -82,9 +82,14 @@ private static Expression CreateFilter(MemberExpression prop, FilterOperator op, private static Expression RobustEquals(MemberExpression prop, ConstantExpression constant) { - if (prop.Type == typeof(bool) && bool.TryParse(constant.Value.ToString(), out var val)) + if (prop.Type == typeof(bool?) && bool.TryParse(constant.Value.ToString(), out var val)) { - return Expression.Equal(prop, Expression.Constant(val)); + var constantNullable = Expression.Convert(Expression.Constant(val), prop.Type); + return Expression.Equal(prop, constantNullable); + } + else if (prop.Type == typeof(bool) && bool.TryParse(constant.Value.ToString(), out val)) + { + return Expression.Equal(prop, Expression.Constant(val)); } if (IsNullableType(prop.Type)) { From 0348d75c9677e80246f592506aeae5c970435ac1 Mon Sep 17 00:00:00 2001 From: khai Date: Fri, 7 Mar 2025 16:16:56 +0700 Subject: [PATCH 3/3] LessThan. GreaterThan, LessThanOrEqual, GreaterThanOrEqual Nullable --- DynamicExpressions/DynamicExpressions.cs | 60 ++++++++++++++++++++---- 1 file changed, 50 insertions(+), 10 deletions(-) diff --git a/DynamicExpressions/DynamicExpressions.cs b/DynamicExpressions/DynamicExpressions.cs index 6e7ea5f..08de818 100644 --- a/DynamicExpressions/DynamicExpressions.cs +++ b/DynamicExpressions/DynamicExpressions.cs @@ -60,8 +60,8 @@ private static Expression CreateFilter(MemberExpression prop, FilterOperator op, return op switch { FilterOperator.Equals => RobustEquals(prop, constant), - FilterOperator.GreaterThan => Expression.GreaterThan(prop, constant), - FilterOperator.LessThan => Expression.LessThan(prop, constant), + FilterOperator.GreaterThan => RobustGreaterThan(prop, constant), + FilterOperator.LessThan => RobustLessThan(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)), @@ -72,36 +72,76 @@ private static Expression CreateFilter(MemberExpression prop, FilterOperator op, 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.GreaterThanOrEqual => RobustGreaterThanOrEqual(prop, constant), + FilterOperator.LessThanOrEqual => RobustLessThanOrEqual(prop, constant), FilterOperator.IsEmpty => Expression.Call(_isNullOrEmtpyMethod, prop), FilterOperator.IsNotEmpty => Expression.Not(Expression.Call(_isNullOrEmtpyMethod, prop)), _ => throw new NotImplementedException() }; } + private static BinaryExpression RobustLessThanOrEqual(MemberExpression prop, ConstantExpression constant) + { + if (IsNullableType(prop.Type)) + { + var constantNullable = Expression.Convert(constant, prop.Type); + return Expression.LessThanOrEqual(prop, constantNullable); + } + return Expression.LessThanOrEqual(prop, constant); + } + + private static BinaryExpression RobustGreaterThanOrEqual(MemberExpression prop, ConstantExpression constant) + { + if (IsNullableType(prop.Type)) + { + var constantNullable = Expression.Convert(constant, prop.Type); + return Expression.GreaterThanOrEqual(prop, constantNullable); + } + return Expression.GreaterThanOrEqual(prop, constant); + } + + private static BinaryExpression RobustGreaterThan(MemberExpression prop, ConstantExpression constant) + { + if (IsNullableType(prop.Type)) + { + var constantNullable = Expression.Convert(constant, prop.Type); + return Expression.GreaterThan(prop, constantNullable); + } + return Expression.GreaterThan(prop, constant); + } + + private static BinaryExpression RobustLessThan(MemberExpression prop, ConstantExpression constant) + { + if (IsNullableType(prop.Type)) + { + var constantNullable = Expression.Convert(constant, prop.Type); + return Expression.LessThan(prop, constantNullable); + } + return Expression.LessThan(prop, constant); + } + private static Expression RobustEquals(MemberExpression prop, ConstantExpression constant) { if (prop.Type == typeof(bool?) && bool.TryParse(constant.Value.ToString(), out var val)) { - var constantNullable = Expression.Convert(Expression.Constant(val), prop.Type); - return Expression.Equal(prop, constantNullable); + var constantNullable = Expression.Convert(Expression.Constant(val), prop.Type); + return Expression.Equal(prop, constantNullable); } else if (prop.Type == typeof(bool) && bool.TryParse(constant.Value.ToString(), out val)) { - return Expression.Equal(prop, Expression.Constant(val)); + return Expression.Equal(prop, Expression.Constant(val)); } if (IsNullableType(prop.Type)) { - var constantNullable = Expression.Convert(constant, prop.Type); - return Expression.Equal(prop, constantNullable); + var constantNullable = Expression.Convert(constant, prop.Type); + return Expression.Equal(prop, constantNullable); } return Expression.Equal(prop, constant); } private static bool IsNullableType(Type t) { - return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>); + return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>); } private static Expression GetContainsMethodCallExpression(MemberExpression prop, ConstantExpression constant)