From ef93176bac3d6d390600a0337bd135bdb1cb24f8 Mon Sep 17 00:00:00 2001 From: AndriiPodkolzin Date: Sat, 3 Aug 2024 10:19:17 +0300 Subject: [PATCH 1/2] Implement set filter + fix text and DateOnly filters --- Ctoss/Builders/Filters/DateFilterBuilder.cs | 6 ++--- Ctoss/Builders/Filters/SetFilterBuilder.cs | 25 ++++++++++++++++++++- Ctoss/Builders/Filters/TextFilterBuilder.cs | 7 +++--- Ctoss/Builders/IPropertyBuilder.cs | 4 ++-- Ctoss/Models/Enums/DateFilterOptions.cs | 4 ++-- 5 files changed, 35 insertions(+), 11 deletions(-) diff --git a/Ctoss/Builders/Filters/DateFilterBuilder.cs b/Ctoss/Builders/Filters/DateFilterBuilder.cs index bbbfb39..e5a1a1d 100644 --- a/Ctoss/Builders/Filters/DateFilterBuilder.cs +++ b/Ctoss/Builders/Filters/DateFilterBuilder.cs @@ -93,10 +93,10 @@ private Expression> GetComparisonExpression(string property, Da DateFilterOptions.NotEquals => Expression.Lambda>( Expression.NotEqual(propertyExpression, dateFromExpression), parameter), - DateFilterOptions.LessThen + DateFilterOptions.LessThan => Expression.Lambda>( Expression.LessThan(propertyExpression, dateFromExpression), parameter), - DateFilterOptions.GreaterThen + DateFilterOptions.GreaterThan => Expression.Lambda>( Expression.GreaterThan(propertyExpression, dateFromExpression), parameter), _ => throw new NotSupportedException($"Date filter type '{condition.Type}' is not supported.") @@ -110,7 +110,7 @@ private static object ParseDateValue(string filterValue, Type propertyType) if (propertyType == typeof(DateTime) || propertyType == typeof(DateTime?)) return DateTime.Parse(filterValue); if (propertyType == typeof(DateOnly) || propertyType == typeof(DateOnly?)) - return DateOnly.Parse(filterValue); + return DateOnly.FromDateTime(DateTime.Parse(filterValue)); if (propertyType == typeof(TimeOnly) || propertyType == typeof(TimeOnly?)) return TimeOnly.Parse(filterValue); if (propertyType == typeof(TimeSpan) || propertyType == typeof(TimeSpan?)) diff --git a/Ctoss/Builders/Filters/SetFilterBuilder.cs b/Ctoss/Builders/Filters/SetFilterBuilder.cs index 72ed70d..9594407 100644 --- a/Ctoss/Builders/Filters/SetFilterBuilder.cs +++ b/Ctoss/Builders/Filters/SetFilterBuilder.cs @@ -1,4 +1,5 @@ using System.Linq.Expressions; +using System.Reflection; using Ctoss.Models.V2; namespace Ctoss.Builders.Filters; @@ -7,6 +8,28 @@ internal class SetFilterBuilder : IPropertyFilterBuilder { public Expression> GetExpression(string property, SetCondition condition) { - throw new NotImplementedException(); + var parameter = Expression.Parameter(typeof(T), "x"); + + // Build: x => x.Property.Contains(condition.Filter) + var propertyType = IPropertyBuilder.GetPropertyType(property); + var propertyExpression = IPropertyBuilder.GetPropertyExpression(property, parameter, propertyType); + + var valueExpression = GetContainsExpression(condition.Values, propertyExpression, propertyType); + + return Expression.Lambda>(valueExpression, parameter); + } + + private Expression GetContainsExpression(List conditionValues, Expression propertyExpression, Type propertyType) + { + return (Expression)GetType().GetMethod(nameof(GetContainsExpressionGeneric), BindingFlags.Instance | BindingFlags.NonPublic) + !.MakeGenericMethod(propertyType) + .Invoke(this, new object[] {propertyExpression, conditionValues})!; + } + + private Expression GetContainsExpressionGeneric(Expression propertyExpression, List conditionValues) + { + var set = new HashSet(conditionValues.Select(x => (T)Convert.ChangeType(x, typeof(T)))); + var setExpression = Expression.Constant(set); + return Expression.Call(setExpression, typeof(HashSet).GetMethod("Contains")!, propertyExpression); } } \ No newline at end of file diff --git a/Ctoss/Builders/Filters/TextFilterBuilder.cs b/Ctoss/Builders/Filters/TextFilterBuilder.cs index 2de7cfa..3f4d2f0 100644 --- a/Ctoss/Builders/Filters/TextFilterBuilder.cs +++ b/Ctoss/Builders/Filters/TextFilterBuilder.cs @@ -12,10 +12,11 @@ public Expression> GetExpression(string property, TextCondition var parameter = Expression.Parameter(typeof(T), "x"); var propertyExpression = IPropertyFilterBuilder .GetPropertyExpression(property, parameter, typeof(string)); + propertyExpression = Expression.Coalesce(propertyExpression, Expression.Constant(string.Empty)); var valueExpression = Expression.Constant(condition.Filter); + ApplyPropertySettings(property, ref propertyExpression, ref valueExpression); - return condition.Type switch { TextFilterOptions.Contains @@ -50,7 +51,7 @@ public Expression> GetExpression(string property, TextCondition private static void ApplyPropertySettings( string property, - ref UnaryExpression propertyExpression, + ref Expression propertyExpression, ref ConstantExpression valueExpression) { var propertySettings = CtossSettings.GetPropertySettings(property); @@ -59,7 +60,7 @@ private static void ApplyPropertySettings( return; } - var memberExpression = (MemberExpression)propertyExpression.Operand; + var memberExpression = propertyExpression is UnaryExpression unary ? unary.Operand : propertyExpression; var propertyToUpper = Expression.Call(memberExpression, nameof(string.ToUpper), Type.EmptyTypes); propertyExpression = Expression.Convert(propertyToUpper, typeof(string)); diff --git a/Ctoss/Builders/IPropertyBuilder.cs b/Ctoss/Builders/IPropertyBuilder.cs index 5c1b4f7..703b52a 100644 --- a/Ctoss/Builders/IPropertyBuilder.cs +++ b/Ctoss/Builders/IPropertyBuilder.cs @@ -7,7 +7,7 @@ namespace Ctoss.Builders; internal interface IPropertyBuilder { - static UnaryExpression GetCompletePropertyExpression(string property, ParameterExpression parameter) + static Expression GetCompletePropertyExpression(string property, ParameterExpression parameter) { // NOTE: first of all, we're trying to get a real property name from the given one. // If we find it, we can use it to work with an expression. Else the given property name will be used. @@ -25,7 +25,7 @@ static UnaryExpression GetCompletePropertyExpression(string property, Paramet return GetPropertyExpression(propertyName, parameter, nullablePropertyType); } - static UnaryExpression GetPropertyExpression( + static Expression GetPropertyExpression( string property, ParameterExpression parameter, Type propertyType) diff --git a/Ctoss/Models/Enums/DateFilterOptions.cs b/Ctoss/Models/Enums/DateFilterOptions.cs index 45eafaf..72d8ff7 100644 --- a/Ctoss/Models/Enums/DateFilterOptions.cs +++ b/Ctoss/Models/Enums/DateFilterOptions.cs @@ -31,14 +31,14 @@ public enum DateFilterOptions /// Option Key: lessThan /// Included by Default: Yes /// - LessThen = 3, + LessThan = 3, /// /// Filters for dates that are greater than the specified date. /// Option Key: greaterThan /// Included by Default: Yes /// - GreaterThen = 4, + GreaterThan = 4, /// /// Filters for dates that are within a specified range. From 6edc3a80a737beb360566d09730eb46f26826cfc Mon Sep 17 00:00:00 2001 From: Vladyslav Bardin Date: Sat, 3 Aug 2024 10:35:00 +0300 Subject: [PATCH 2/2] Fix tests --- Ctoss.Tests/DateFilterTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Ctoss.Tests/DateFilterTests.cs b/Ctoss.Tests/DateFilterTests.cs index cb86de2..c21d974 100644 --- a/Ctoss.Tests/DateFilterTests.cs +++ b/Ctoss.Tests/DateFilterTests.cs @@ -40,7 +40,7 @@ public void DateFilter_GreaterThen_Success() { FilterType = "date", DateFrom = "02/02/2023", - Type = "GreaterThen" + Type = "GreaterThan" }; var expr = _filterBuilder.GetExpression("DateTimeProperty", filter)!; @@ -72,7 +72,7 @@ public void DateFilter_LessThen_Success() { FilterType = "date", DateFrom = "01/01/2023", - Type = "LessThen" + Type = "LessThan" }; var expr = _filterBuilder.GetExpression("DateTimeProperty", filter)!; @@ -158,7 +158,7 @@ public void DateFilter_Composed_Success() { DateFrom = "03/03/2024", FilterType = "date", - Type = DateFilterOptions.LessThen + Type = DateFilterOptions.LessThan }; var filter = new FilterModel