diff --git a/PrimeNG.TableFilter.Test/PrimeNG.TableFilter.Test.csproj b/PrimeNG.TableFilter.Test/PrimeNG.TableFilter.Test.csproj index 4e5942b..58e58e1 100644 --- a/PrimeNG.TableFilter.Test/PrimeNG.TableFilter.Test.csproj +++ b/PrimeNG.TableFilter.Test/PrimeNG.TableFilter.Test.csproj @@ -10,7 +10,7 @@ - + diff --git a/PrimeNG.TableFilter/Core/ITableFilterManager.cs b/PrimeNG.TableFilter/Core/ITableFilterManager.cs index 88c9980..c42b92a 100644 --- a/PrimeNG.TableFilter/Core/ITableFilterManager.cs +++ b/PrimeNG.TableFilter/Core/ITableFilterManager.cs @@ -9,6 +9,7 @@ public interface ITableFilterManager void MultipleOrderDataSet(TableFilterModel tableFilterPayload); void SingleOrderDataSet(TableFilterModel tableFilterPayload); void FilterDataSet(string key, TableFilterContext value); + void FilterDataSet(string key, TableFilterContext value, OperatorEnumeration opMode); void FiltersDataSet(string key, IEnumerable values); void ExecuteFilter(); IQueryable GetResult(); diff --git a/PrimeNG.TableFilter/Core/LinqOperator.cs b/PrimeNG.TableFilter/Core/LinqOperator.cs index 742cc0d..bbb9e6d 100644 --- a/PrimeNG.TableFilter/Core/LinqOperator.cs +++ b/PrimeNG.TableFilter/Core/LinqOperator.cs @@ -49,17 +49,64 @@ public void AddFilterProperty( OperatorEnumeration operatorAction, bool isNegation = false) { - var property = _context.DataSetType.GetProperty(propertyName); + PropertyInfo property = null; + foreach (var item in propertyName.Split('.')) + { + if (property == null) + property = _context.DataSetType.GetProperty(item.FirstCharToUpper()); + else + property = property.PropertyType.GetProperty(item.FirstCharToUpper()); + } var propertyType = property?.PropertyType; if (propertyType == null) return; - if (!IsPropertyTypeAndFilterMatchModeValid(propertyType, extensionMethod)) + if (propertyType != typeof(string) && extensionMethod == LinqOperatorConstants.ConstantContains) + { + if (propertyType == typeof(DateTime) || propertyType == typeof(DateTime?)) + { + extensionMethod = LinqOperatorConstants.ConstantDateIs; + if (DateTime.TryParse(propertyValue.ToString(), out var currentDate)) + propertyValue = currentDate; + else + return; + } + else if (propertyType == typeof(bool) || propertyType == typeof(bool?)) + { + extensionMethod = LinqOperatorConstants.ConstantEquals; + if (bool.TryParse(propertyValue.ToString(),out var currentBoolean)) + propertyValue = currentBoolean; + else + return; + } + else if (IsNumericType(propertyType)) + { + extensionMethod = LinqOperatorConstants.ConstantEquals; + if ((propertyType == typeof(short) || propertyType == typeof(short?)) && short.TryParse(propertyValue.ToString(), out var currentShort)) + propertyValue = currentShort; + else if ((propertyType == typeof(int) || propertyType == typeof(int?)) && int.TryParse(propertyValue.ToString(), out var currentInt)) + propertyValue = currentInt; + else if ((propertyType == typeof(long) || propertyType == typeof(long?)) && long.TryParse(propertyValue.ToString(), out var currentLong)) + propertyValue = currentLong; + else if ((propertyType == typeof(float) || propertyType == typeof(float?)) && float.TryParse(propertyValue.ToString(), out var currentFloat)) + propertyValue = currentFloat; + else if ((propertyType == typeof(double) || propertyType == typeof(double?)) && double.TryParse(propertyValue.ToString(), out var currentDouble)) + propertyValue = currentDouble; + else if ((propertyType == typeof(decimal) || propertyType == typeof(decimal?)) && decimal.TryParse(propertyValue.ToString(), out var currentDecimal)) + propertyValue = currentDecimal; + else if ((propertyType.IsEnum || (Nullable.GetUnderlyingType(propertyType)?.IsEnum == true)) && int.TryParse(propertyValue.ToString(), out var currentEnum)) + propertyValue = currentEnum; + else + return; + } + } + + if (!IsPropertyTypeAndFilterMatchModeValid(propertyType, extensionMethod)) throw new ArgumentException($"Property ${propertyName} not support method ${extensionMethod}"); var castValue = ObjectCasterUtil.CastPropertiesType(property, propertyValue); - var propertyConstant = Expression.Constant(castValue, propertyType); + var propertyConstant = propertyType.IsEnum ? Expression.Constant(castValue, typeof(int)) : Expression.Constant(castValue, propertyType); if (IsNullableType(propertyType)) { @@ -90,8 +137,14 @@ public void AddFilterProperty( return; case string _: { - var propertyAccess = Expression.MakeMemberAccess(_context.ParameterExpression, - property ?? throw new InvalidOperationException()); + MemberExpression propertyAccess = null; + foreach (var item in propertyName.Split('.')) + { + if (propertyAccess == null) + propertyAccess = Expression.PropertyOrField(_context.ParameterExpression, item.FirstCharToUpper()); + else + propertyAccess = Expression.PropertyOrField(propertyAccess, item.FirstCharToUpper()); + } var methodInfo = propertyType.GetMethod(extensionMethod, new[] { propertyType }); if (isNegation) @@ -193,13 +246,31 @@ public void ThenByDescending(string orderProperty) => private void BaseOrderExecute(string command, string orderByProperty) { - var property = _context.DataSetType.GetProperty(orderByProperty); - var propertyAccess = - Expression.MakeMemberAccess(_context.ParameterExpression, - property ?? throw new InvalidOperationException()); + PropertyInfo property = null; + foreach (var item in orderByProperty.Split('.')) + { + if (property == null) + property = _context.DataSetType.GetProperty(item.FirstCharToUpper()); + else + property = property.PropertyType.GetProperty(item.FirstCharToUpper()); + } + var propertyType = property?.PropertyType; + + if (propertyType == null) + return; + + MemberExpression propertyAccess = null; + foreach (var item in orderByProperty.Split('.')) + { + if (propertyAccess == null) + propertyAccess = Expression.PropertyOrField(_context.ParameterExpression, item); + else + propertyAccess = Expression.PropertyOrField(propertyAccess, item); + } + var orderByExpression = Expression.Lambda(propertyAccess, _context.ParameterExpression); var resultExpression = Expression.Call(typeof(Queryable), command, - new[] { _context.DataSetType, property.PropertyType }, + new[] { _context.DataSetType, propertyType }, _context.DataSet.Expression, Expression.Quote(orderByExpression)); _context.DataSet = _context.DataSet.Provider.CreateQuery(resultExpression); } @@ -220,8 +291,13 @@ private static bool IsNullableType(Type propertyType) /// True if nullable, otherwise False private static bool IsNumericType(Type propertyType) { - return (propertyType == typeof(short) || propertyType == typeof(short?) || propertyType == typeof(int) || propertyType == typeof(int?) || propertyType == typeof(long) || propertyType == typeof(long?) - || propertyType == typeof(float) || propertyType == typeof(float?) || propertyType == typeof(double) || propertyType == typeof(double?) || propertyType == typeof(decimal) || propertyType == typeof(decimal?)); + return ((propertyType == typeof(short) || propertyType == typeof(short?)) || + (propertyType == typeof(int) || propertyType == typeof(int?)) || + (propertyType == typeof(long) || propertyType == typeof(long?)) || + (propertyType == typeof(float) || propertyType == typeof(float?)) || + (propertyType == typeof(double) || propertyType == typeof(double?)) || + (propertyType == typeof(decimal) || propertyType == typeof(decimal?)) || + (propertyType.IsEnum || (Nullable.GetUnderlyingType(propertyType)?.IsEnum == true))); } /// /// Checks if for provided , is valid diff --git a/PrimeNG.TableFilter/Core/TableFilterManager.cs b/PrimeNG.TableFilter/Core/TableFilterManager.cs index c335295..a846a4e 100644 --- a/PrimeNG.TableFilter/Core/TableFilterManager.cs +++ b/PrimeNG.TableFilter/Core/TableFilterManager.cs @@ -49,16 +49,16 @@ public void MultipleOrderDataSet(TableFilterModel tableFilterPayload) { case (int)SortingEnumeration.OrderByAsc: if (o.i == 0) - _linqOperator.OrderBy(o.value.Field.FirstCharToUpper()); + _linqOperator.OrderBy(o.value.Field); else - _linqOperator.ThenBy(o.value.Field.FirstCharToUpper()); + _linqOperator.ThenBy(o.value.Field); break; case (int)SortingEnumeration.OrderByDesc: if (o.i == 0) - _linqOperator.OrderByDescending(o.value.Field.FirstCharToUpper()); + _linqOperator.OrderByDescending(o.value.Field); else - _linqOperator.ThenByDescending(o.value.Field.FirstCharToUpper()); + _linqOperator.ThenByDescending(o.value.Field); break; default: @@ -77,11 +77,11 @@ public void SingleOrderDataSet(TableFilterModel tableFilterPayload) switch (tableFilterPayload.SortOrder) { case (int)SortingEnumeration.OrderByAsc: - _linqOperator.OrderBy(tableFilterPayload.SortField.FirstCharToUpper()); + _linqOperator.OrderBy(tableFilterPayload.SortField); break; case (int)SortingEnumeration.OrderByDesc: - _linqOperator.OrderByDescending(tableFilterPayload.SortField.FirstCharToUpper()); + _linqOperator.OrderByDescending(tableFilterPayload.SortField); break; default: @@ -97,6 +97,15 @@ public void SingleOrderDataSet(TableFilterModel tableFilterPayload) public void FilterDataSet(string key, TableFilterContext value) => BaseFilterDataSet(key, value, OperatorEnumeration.None); + /// + /// Set filter condition data to LINQ Operation context + /// + /// Name of property + /// PrimeNG filter context + /// PrimeNG filter connection operator + public void FilterDataSet(string key, TableFilterContext value, OperatorEnumeration filterOperator) + => BaseFilterDataSet(key, value, filterOperator); + /// /// The base method for set filter condition data to LINQ Operation context /// @@ -112,87 +121,85 @@ private void BaseFilterDataSet(string key, TableFilterContext value, OperatorEnu switch (value.MatchMode) { case ConstantTypeMatchModeStartsWith: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantStartsWith , operatorAction); break; case ConstantTypeMatchModeContains: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantContains , operatorAction); break; case ConstantTypeMatchModeIn: - _linqOperator.AddFilterListProperty(key.FirstCharToUpper(), value.Value + _linqOperator.AddFilterListProperty(key, value.Value , operatorAction); break; case ConstantTypeMatchModeEndsWith: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantEndsWith , OperatorEnumeration.None); break; case ConstantTypeMatchModeEquals: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantEquals , operatorAction); break; case ConstantTypeMatchModeNotContains: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantContains , OperatorEnumeration.None, true); break; case ConstantTypeMatchModeNotEquals: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantEquals , operatorAction, true); break; case ConstantTypeMatchModeDateIs: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantDateIs , operatorAction); break; case ConstantTypeMatchModeDateIsNot: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantDateIs , operatorAction, true); break; case ConstantTypeMatchModeDateBefore: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantBefore , operatorAction); break; case ConstantTypeMatchModeDateAfter: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantAfter , operatorAction); break; case ConstantTypeMatchModeLessThan: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantLessThan , operatorAction); break; case ConstantTypeMatchModeLessOrEqualsThan: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantLessThanOrEqual , operatorAction); break; case ConstantTypeMatchModeGreaterThan: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantGreaterThan , operatorAction); break; case ConstantTypeMatchModeGreaterOrEqualsThan: - _linqOperator.AddFilterProperty(key.FirstCharToUpper(), value.Value, + _linqOperator.AddFilterProperty(key, value.Value, LinqOperatorConstants.ConstantGreaterThanOrEqual , operatorAction); break; - - default: throw new System.ArgumentException("Invalid Match mode!"); } diff --git a/PrimeNG.TableFilter/Models/TableFilterModel.cs b/PrimeNG.TableFilter/Models/TableFilterModel.cs index d5ff3c4..1fd0a3b 100644 --- a/PrimeNG.TableFilter/Models/TableFilterModel.cs +++ b/PrimeNG.TableFilter/Models/TableFilterModel.cs @@ -5,11 +5,11 @@ namespace PrimeNG.TableFilter.Models { public class TableFilterModel { - public Dictionary Filters { get; set; } - public int First { get; set; } - public int Rows { get; set; } + public Dictionary Filters { get; set; } = new Dictionary(); + public int First { get; set; } = 0; + public int Rows { get; set; } = 10; public string SortField { get; set; } public int SortOrder { get; set; } - public List MultiSortMeta { get; set; } + public List MultiSortMeta { get; set; } = new List(); } } diff --git a/PrimeNG.TableFilter/PrimeNG.TableFilter.csproj b/PrimeNG.TableFilter/PrimeNG.TableFilter.csproj index 1f90658..de8d20c 100644 --- a/PrimeNG.TableFilter/PrimeNG.TableFilter.csproj +++ b/PrimeNG.TableFilter/PrimeNG.TableFilter.csproj @@ -9,7 +9,7 @@ https://github.com/Kusumoto/PrimeNG.TableFilter 2.1.2 2.1.2 - net462;netstandard2.1;net6.0;net5.0 + net462;netstandard2.1;net6.0;net5.0;net7.0 false LICENSE.md 2.1.2 @@ -21,8 +21,8 @@ - - + + diff --git a/PrimeNG.TableFilter/PrimeNGTableFilterExtension.cs b/PrimeNG.TableFilter/PrimeNGTableFilterExtension.cs index 4419585..b126097 100644 --- a/PrimeNG.TableFilter/PrimeNGTableFilterExtension.cs +++ b/PrimeNG.TableFilter/PrimeNGTableFilterExtension.cs @@ -42,9 +42,25 @@ public static IQueryable PrimengTableFilter(this IQueryable dataSet, { ITableFilterManager tableFilterManager = new TableFilterManager(dataSet); - if (tableFilterPayload.Filters != null && tableFilterPayload.Filters.Any()) { + if (tableFilterPayload.Filters.ContainsKey("global")) + { + var filterPayload = tableFilterPayload.Filters["global"]?.ToString(); + if (filterPayload != null) + { + var filterToken = JToken.Parse(filterPayload); + var filter = filterToken.ToObject(); + if (filter != null) + { + foreach (var filterContext in tableFilterPayload.Filters) + { + tableFilterManager.FilterDataSet(filterContext.Key, filter, OperatorEnumeration.Or); + } + } + } + } + foreach (var filterContext in tableFilterPayload.Filters) { var filterPayload = filterContext.Value.ToString(); @@ -52,17 +68,17 @@ public static IQueryable PrimengTableFilter(this IQueryable dataSet, switch (filterToken) { case JArray _: - { - var filters = filterToken.ToObject>(); - tableFilterManager.FiltersDataSet(filterContext.Key, filters); - break; - } + { + var filters = filterToken.ToObject>(); + tableFilterManager.FiltersDataSet(filterContext.Key, filters); + break; + } case JObject _: - { + { var filter = filterToken.ToObject(); tableFilterManager.FilterDataSet(filterContext.Key, filter); - break; - } + break; + } } } tableFilterManager.ExecuteFilter(); diff --git a/PrimeNG.TableFilter/Properties/PublishProfiles/Net4.6.2.pubxml b/PrimeNG.TableFilter/Properties/PublishProfiles/Net4.6.2.pubxml new file mode 100644 index 0000000..5c62fe3 --- /dev/null +++ b/PrimeNG.TableFilter/Properties/PublishProfiles/Net4.6.2.pubxml @@ -0,0 +1,14 @@ + + + + + Release + Any CPU + bin\Release\net462\publish\ + FileSystem + <_TargetId>Folder + net462 + + \ No newline at end of file diff --git a/PrimeNG.TableFilter/Properties/PublishProfiles/Net5.0.pubxml b/PrimeNG.TableFilter/Properties/PublishProfiles/Net5.0.pubxml new file mode 100644 index 0000000..2a5fe8e --- /dev/null +++ b/PrimeNG.TableFilter/Properties/PublishProfiles/Net5.0.pubxml @@ -0,0 +1,15 @@ + + + + + Release + Any CPU + bin\Release\net5.0\publish\ + FileSystem + <_TargetId>Folder + net5.0 + false + + \ No newline at end of file diff --git a/PrimeNG.TableFilter/Properties/PublishProfiles/Net6.0.pubxml b/PrimeNG.TableFilter/Properties/PublishProfiles/Net6.0.pubxml new file mode 100644 index 0000000..95b2920 --- /dev/null +++ b/PrimeNG.TableFilter/Properties/PublishProfiles/Net6.0.pubxml @@ -0,0 +1,15 @@ + + + + + Release + Any CPU + bin\Release\net6.0\publish\ + FileSystem + <_TargetId>Folder + net6.0 + false + + \ No newline at end of file diff --git a/PrimeNG.TableFilter/Properties/PublishProfiles/Net7.0.pubxml b/PrimeNG.TableFilter/Properties/PublishProfiles/Net7.0.pubxml new file mode 100644 index 0000000..d348e50 --- /dev/null +++ b/PrimeNG.TableFilter/Properties/PublishProfiles/Net7.0.pubxml @@ -0,0 +1,15 @@ + + + + + Release + Any CPU + bin\Release\net7.0\publish\ + FileSystem + <_TargetId>Folder + net7.0 + false + + \ No newline at end of file diff --git a/PrimeNG.TableFilter/Properties/PublishProfiles/NetStandard2.1.pubxml b/PrimeNG.TableFilter/Properties/PublishProfiles/NetStandard2.1.pubxml new file mode 100644 index 0000000..a1ffed3 --- /dev/null +++ b/PrimeNG.TableFilter/Properties/PublishProfiles/NetStandard2.1.pubxml @@ -0,0 +1,14 @@ + + + + + Release + Any CPU + bin\Release\netstandard2.1\publish\ + FileSystem + <_TargetId>Folder + netstandard2.1 + + \ No newline at end of file diff --git a/PrimeNG.TableFilter/Utils/ObjectCasterUtil.cs b/PrimeNG.TableFilter/Utils/ObjectCasterUtil.cs index 4375141..1589681 100644 --- a/PrimeNG.TableFilter/Utils/ObjectCasterUtil.cs +++ b/PrimeNG.TableFilter/Utils/ObjectCasterUtil.cs @@ -42,7 +42,9 @@ public static object CastPropertiesTypeList(PropertyInfo property, object value) if (property?.PropertyType == typeof(decimal)) return arrayCast.ToObject>(); if (property?.PropertyType == typeof(decimal?)) - return arrayCast.ToObject>(); + return arrayCast.ToObject>(); + if (Nullable.GetUnderlyingType(property?.PropertyType).IsEnum) + return arrayCast.ToObject>(); return arrayCast.ToObject>(); } @@ -81,8 +83,9 @@ public static object CastPropertiesType(PropertyInfo property, object value) if (property?.PropertyType == typeof(decimal)) return Convert.ToDecimal(value); if (property?.PropertyType == typeof(decimal?)) - return Convert.ToDecimal(value); - + return Convert.ToDecimal(value); + if (property?.PropertyType.IsEnum == true) + return Convert.ToInt32(value); return value.ToString(); } diff --git a/PrimeNG.TableFilter/Utils/StringUtil.cs b/PrimeNG.TableFilter/Utils/StringUtil.cs index 88dd80d..52fb264 100644 --- a/PrimeNG.TableFilter/Utils/StringUtil.cs +++ b/PrimeNG.TableFilter/Utils/StringUtil.cs @@ -11,7 +11,7 @@ public static string FirstCharToUpper(this string input) { case null: throw new ArgumentNullException(nameof(input)); case "": throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)); - default: return input.First().ToString().ToUpper() + input.Substring(1); + default: return input.First().ToString().ToUpperInvariant() + input.Substring(1); } } }