< Summary

Information
Class: Services.FiltersServices.QueryableDataFilter<T>
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/FiltersServices/QueryableDataFilter.cs
Line coverage
100%
Covered lines: 134
Uncovered lines: 0
Coverable lines: 134
Total lines: 335
Line coverage: 100%
Branch coverage
95%
Covered branches: 46
Total branches: 48
Branch coverage: 95.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()50%44100%
FilterByInt(...)100%22100%
FilterByDate(...)100%22100%
FilterByString(...)100%44100%
FilterByDecimal(...)100%22100%
FilterByDouble(...)100%22100%
FilterByPeriod(...)100%1010100%
FilterByEnum(...)100%22100%
FilterByStringExactSimple(...)100%66100%
FilterByStringList(...)100%1414100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/FiltersServices/QueryableDataFilter.cs

#LineLine coverage
 1using System.Linq.Expressions;
 2using System.Reflection;
 3
 4namespace Services.FiltersServices
 5{
 6    public class QueryableDataFilter<T> : IQueryableDataFilter<T>
 7    {
 48        private static readonly MethodInfo StringToLowerMethod =
 49            typeof(string).GetMethod(nameof(string.ToLower), Type.EmptyTypes)
 410            ?? throw new InvalidOperationException("string.ToLower not found");
 11
 412        private static readonly MethodInfo StringContainsMethod =
 413            typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string) })
 414            ?? throw new InvalidOperationException("string.Contains not found");
 15
 16        public IQueryable<T> FilterByInt(
 17            IQueryable<T> data,
 18            Expression<Func<T, int?>> propertySelector,
 19            int? filterValue)
 20        {
 1321            if (filterValue != null)
 22            {
 1223                var param = Expression.Parameter(typeof(T), "item");
 1224                var property = Expression.Invoke(propertySelector, param);
 1225                var notNullExpression = Expression.NotEqual(property, Expression.Constant(null, typeof(int?)));
 1226                var equalExpression = Expression.Equal(property, Expression.Constant(filterValue, typeof(int?)));
 1227                var andExpression = Expression.AndAlso(notNullExpression, equalExpression);
 1228                var lambda = Expression.Lambda<Func<T, bool>>(andExpression, param);
 29
 1230                return data.Where(lambda);
 31            }
 32            else
 33            {
 134                return data;
 35            }
 36        }
 37
 38
 39        public IQueryable<T> FilterByDate(
 40            IQueryable<T> data,
 41            Expression<Func<T, DateTime?>> propertySelector,
 42            DateTime? filterDate)
 43        {
 3644            if (filterDate != null)
 45            {
 1046                DateTime filterDateDay = filterDate.Value.Date;
 1047                var param = Expression.Parameter(typeof(T), "item");
 1048                var property = Expression.Invoke(propertySelector, param);
 1049                var propertyDate = Expression.Property(property, "Value");
 1050                var propertyDateDate = Expression.Property(propertyDate, "Date");
 1051                var filterDateConstant = Expression.Constant(filterDateDay, typeof(DateTime));
 1052                var equalExpression = Expression.Equal(propertyDateDate, filterDateConstant);
 1053                var notNullExpression = Expression.NotEqual(property, Expression.Constant(null, typeof(DateTime?)));
 1054                var andExpression = Expression.AndAlso(notNullExpression, equalExpression);
 1055                var lambda = Expression.Lambda<Func<T, bool>>(andExpression, param);
 56
 1057                return data.Where(lambda);
 58            }
 59            else
 60            {
 2661                return data;
 62            }
 63        }
 64
 65
 66        public IQueryable<T> FilterByString(
 67            IQueryable<T> data,
 68            Expression<Func<T, string?>> propertySelector,
 69            string? filterValue,
 70            bool exactMatch = false)
 71        {
 6972            if (!string.IsNullOrEmpty(filterValue))
 73            {
 2574                var filterValueLower = filterValue.ToLower();
 2575                var param = Expression.Parameter(typeof(T), "item");
 2576                var property = Expression.Invoke(propertySelector, param);
 2577                var notNullExpression = Expression.NotEqual(property, Expression.Constant(null, typeof(string)));
 78                Expression conditionExpression;
 79
 2580                if (exactMatch)
 81                {
 182                    var toLowerProperty = Expression.Call(property, StringToLowerMethod);
 183                    var toLowerFilterValue = Expression.Constant(filterValueLower);
 184                    conditionExpression = Expression.Equal(toLowerProperty, toLowerFilterValue);
 85                }
 86                else
 87                {
 2488                    var toLowerProperty = Expression.Call(property, StringToLowerMethod);
 2489                    var toLowerFilterValue = Expression.Constant(filterValueLower);
 2490                    conditionExpression = Expression.Call(toLowerProperty, StringContainsMethod, toLowerFilterValue);
 91                }
 92
 2593                var andExpression = Expression.AndAlso(notNullExpression, conditionExpression);
 2594                var lambda = Expression.Lambda<Func<T, bool>>(andExpression, param);
 95
 2596                return data.Where(lambda);
 97            }
 98            else
 99            {
 44100                return data;
 101            }
 102        }
 103
 104
 105        public IQueryable<T> FilterByDecimal(
 106            IQueryable<T> data,
 107            Expression<Func<T, decimal?>> propertySelector,
 108            decimal? filterValue,
 109            decimal tolerance = 1)
 110        {
 40111            if (filterValue != null)
 112            {
 3113                var param = Expression.Parameter(typeof(T), "item");
 3114                var property = Expression.Invoke(propertySelector, param);
 3115                var notNullExpression = Expression.NotEqual(property, Expression.Constant(null, typeof(decimal?)));
 116
 3117                var lowerBound = Expression.Constant(filterValue.Value - tolerance, typeof(decimal?));
 3118                var upperBound = Expression.Constant(filterValue.Value + tolerance, typeof(decimal?));
 119
 3120                var greaterThanOrEqualExpression = Expression.GreaterThanOrEqual(property, lowerBound);
 3121                var lessThanOrEqualExpression = Expression.LessThanOrEqual(property, upperBound);
 122
 3123                var betweenExpression = Expression.AndAlso(greaterThanOrEqualExpression, lessThanOrEqualExpression);
 3124                var andExpression = Expression.AndAlso(notNullExpression, betweenExpression);
 125
 3126                var lambda = Expression.Lambda<Func<T, bool>>(andExpression, param);
 127
 3128                return data.Where(lambda);
 129            }
 130            else
 131            {
 37132                return data;
 133            }
 134        }
 135
 136
 137        public IQueryable<T> FilterByDouble(
 138            IQueryable<T> data,
 139            Expression<Func<T, double?>> propertySelector,
 140            double? filterValue)
 141        {
 6142            if (filterValue != null)
 143            {
 1144                var param = Expression.Parameter(typeof(T), "item");
 1145                var property = Expression.Invoke(propertySelector, param);
 1146                var notNullExpression = Expression.NotEqual(property, Expression.Constant(null, typeof(double?)));
 1147                var equalExpression = Expression.Equal(property, Expression.Constant(filterValue, typeof(double?)));
 1148                var andExpression = Expression.AndAlso(notNullExpression, equalExpression);
 1149                var lambda = Expression.Lambda<Func<T, bool>>(andExpression, param);
 150
 1151                return data.Where(lambda);
 152            }
 153            else
 154            {
 5155                return data;
 156            }
 157        }
 158
 159        public IQueryable<T> FilterByPeriod(
 160            IQueryable<T> data,
 161            Expression<Func<T, DateTime>> propertySelector,
 162            DateTime? startDate,
 163            DateTime? endDate)
 164        {
 4165            var param = Expression.Parameter(typeof(T), "item");
 4166            var property = Expression.Invoke(propertySelector, param);
 167
 4168            Expression? combinedExpression = null;
 169
 4170            if (startDate != null && endDate != null)
 171            {
 1172                var startDateConstant = Expression.Constant(startDate.Value, typeof(DateTime));
 1173                var endDateConstant = Expression.Constant(endDate.Value, typeof(DateTime));
 174
 1175                var greaterThanOrEqualExpression = Expression.GreaterThanOrEqual(property, startDateConstant);
 1176                var lessThanOrEqualExpression = Expression.LessThanOrEqual(property, endDateConstant);
 177
 1178                combinedExpression = Expression.AndAlso(greaterThanOrEqualExpression, lessThanOrEqualExpression);
 179            }
 3180            else if (startDate != null)
 181            {
 1182                var startDateConstant = Expression.Constant(startDate.Value, typeof(DateTime));
 1183                combinedExpression = Expression.GreaterThanOrEqual(property, startDateConstant);
 184            }
 2185            else if (endDate != null)
 186            {
 1187                var endDateConstant = Expression.Constant(endDate.Value, typeof(DateTime));
 1188                combinedExpression = Expression.LessThanOrEqual(property, endDateConstant);
 189            }
 190
 4191            if (combinedExpression != null)
 192            {
 3193                var lambda = Expression.Lambda<Func<T, bool>>(combinedExpression, param);
 3194                return data.Where(lambda);
 195            }
 196
 1197            return data;
 198        }
 199
 200
 201        public IQueryable<T> FilterByEnum<TEnum>(
 202            IQueryable<T> data,
 203            Expression<Func<T, TEnum?>> propertySelector,
 204            TEnum? filterValue)
 205            where TEnum : struct, Enum
 206        {
 24207            if (filterValue != null)
 208            {
 11209                var param = Expression.Parameter(typeof(T), "item");
 11210                var property = Expression.Invoke(propertySelector, param);
 11211                var notNullExpression = Expression.NotEqual(property, Expression.Constant(null, typeof(TEnum?)));
 11212                var equalExpression = Expression.Equal(property, Expression.Constant(filterValue, typeof(TEnum?)));
 11213                var andExpression = Expression.AndAlso(notNullExpression, equalExpression);
 11214                var lambda = Expression.Lambda<Func<T, bool>>(andExpression, param);
 215
 11216                return data.Where(lambda);
 217            }
 218            else
 219            {
 13220                return data;
 221            }
 222        }
 223
 224        public IQueryable<T> FilterByStringExactSimple(
 225            IQueryable<T> data,
 226            Expression<Func<T, string?>> propertySelector,
 227            string? filterValue,
 228            bool ignoreCase = true)
 229        {
 36230            if (string.IsNullOrEmpty(filterValue))
 231            {
 34232                return data;
 233            }
 234
 2235            var filterValueNormalized = ignoreCase ? filterValue.ToLower() : filterValue;
 236
 2237            var param = Expression.Parameter(typeof(T), "item");
 2238            var property = Expression.Invoke(propertySelector, param);
 239
 240            // Простая проверка на null (без глубокой проверки вложенных свойств)
 2241            var notNullExpression = Expression.NotEqual(property, Expression.Constant(null, typeof(string)));
 242            Expression conditionExpression;
 243
 2244            if (ignoreCase)
 245            {
 1246                var toLowerProperty = Expression.Call(property, StringToLowerMethod);
 1247                var toLowerFilterValue = Expression.Constant(filterValueNormalized);
 1248                conditionExpression = Expression.Equal(toLowerProperty, toLowerFilterValue);
 249            }
 250            else
 251            {
 1252                conditionExpression = Expression.Equal(property, Expression.Constant(filterValueNormalized));
 253            }
 254
 2255            var andExpression = Expression.AndAlso(notNullExpression, conditionExpression);
 2256            var lambda = Expression.Lambda<Func<T, bool>>(andExpression, param);
 257
 2258            return data.Where(lambda);
 259        }
 260
 261        /// <summary>
 262        /// Фильтрует данные по списку строк с использованием логики OR (элемент должен соответствовать хотя бы одному з
 263        /// </summary>
 264        /// <param name="data">Исходные данные для фильтрации</param>
 265        /// <param name="propertySelector">Селектор свойства для фильтрации</param>
 266        /// <param name="filterValues">Список значений для фильтрации</param>
 267        /// <param name="exactMatch">Если true - точное совпадение, если false - частичное (Contains)</param>
 268        /// <returns>Отфильтрованные данные</returns>
 269        public IQueryable<T> FilterByStringList(
 270            IQueryable<T> data,
 271            Expression<Func<T, string?>> propertySelector,
 272            IEnumerable<string> filterValues,
 273            bool exactMatch = false)
 274        {
 275            // Если список пустой или null - возвращаем данные без фильтрации
 6276            if (filterValues == null || !filterValues.Any())
 277            {
 2278                return data;
 279            }
 280
 281            // Убираем пустые и null значения из списка
 14282            var nonEmptyValues = filterValues.Where(v => !string.IsNullOrEmpty(v)).ToList();
 283
 4284            if (!nonEmptyValues.Any())
 285            {
 1286                return data;
 287            }
 288
 3289            var param = Expression.Parameter(typeof(T), "item");
 3290            var property = Expression.Invoke(propertySelector, param);
 3291            var notNullExpression = Expression.NotEqual(property, Expression.Constant(null, typeof(string)));
 292
 293            // Создаем список условий для каждого значения
 3294            var conditions = new List<Expression>();
 295
 3296            var toLowerMethod = StringToLowerMethod;
 3297            var containsMethod = StringContainsMethod;
 298
 18299            foreach (var filterValue in nonEmptyValues)
 300            {
 301                Expression valueCondition;
 302
 6303                if (exactMatch)
 304                {
 305                    // Точное совпадение (case-insensitive)
 2306                    var toLowerProperty = Expression.Call(property, toLowerMethod);
 2307                    var toLowerFilterValue = Expression.Constant(filterValue.ToLower());
 2308                    valueCondition = Expression.Equal(toLowerProperty, toLowerFilterValue);
 309                }
 310                else
 311                {
 312                    // Частичное совпадение (Contains, case-insensitive)
 4313                    var toLowerProperty = Expression.Call(property, toLowerMethod);
 4314                    var toLowerFilterValue = Expression.Constant(filterValue.ToLower());
 4315                    valueCondition = Expression.Call(toLowerProperty, containsMethod, toLowerFilterValue);
 316                }
 317
 318                // Объединяем проверку на null и условие для значения
 6319                var valueAndNotNull = Expression.AndAlso(notNullExpression, valueCondition);
 6320                conditions.Add(valueAndNotNull);
 321            }
 322
 323            // Объединяем все условия через OR
 3324            Expression orExpression = conditions[0];
 12325            for (int i = 1; i < conditions.Count; i++)
 326            {
 3327                orExpression = Expression.OrElse(orExpression, conditions[i]);
 328            }
 329
 3330            var lambda = Expression.Lambda<Func<T, bool>>(orExpression, param);
 331
 3332            return data.Where(lambda);
 333        }
 334    }
 335}

Methods/Properties

.cctor()
FilterByInt(System.Linq.IQueryable`1<T>,System.Linq.Expressions.Expression`1<System.Func`2<T,System.Nullable`1<System.Int32>>>,System.Nullable`1<System.Int32>)
FilterByDate(System.Linq.IQueryable`1<T>,System.Linq.Expressions.Expression`1<System.Func`2<T,System.Nullable`1<System.DateTime>>>,System.Nullable`1<System.DateTime>)
FilterByString(System.Linq.IQueryable`1<T>,System.Linq.Expressions.Expression`1<System.Func`2<T,System.String>>,System.String,System.Boolean)
FilterByDecimal(System.Linq.IQueryable`1<T>,System.Linq.Expressions.Expression`1<System.Func`2<T,System.Nullable`1<System.Decimal>>>,System.Nullable`1<System.Decimal>,System.Decimal)
FilterByDouble(System.Linq.IQueryable`1<T>,System.Linq.Expressions.Expression`1<System.Func`2<T,System.Nullable`1<System.Double>>>,System.Nullable`1<System.Double>)
FilterByPeriod(System.Linq.IQueryable`1<T>,System.Linq.Expressions.Expression`1<System.Func`2<T,System.DateTime>>,System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>)
FilterByEnum(System.Linq.IQueryable`1<T>,System.Linq.Expressions.Expression`1<System.Func`2<T,System.Nullable`1<TEnum>>>,System.Nullable`1<TEnum>)
FilterByStringExactSimple(System.Linq.IQueryable`1<T>,System.Linq.Expressions.Expression`1<System.Func`2<T,System.String>>,System.String,System.Boolean)
FilterByStringList(System.Linq.IQueryable`1<T>,System.Linq.Expressions.Expression`1<System.Func`2<T,System.String>>,System.Collections.Generic.IEnumerable`1<System.String>,System.Boolean)