Как усечь десятичное значение, а затем сравнить в Linq.Expressions c# - PullRequest
0 голосов
/ 21 февраля 2020

У меня есть класс помощника по выражениям, который я использую для фильтрации на стороне сервера для имен столбцов, которые я проверяю динамически, таких как:

 public static Expression<Func<TItem, bool>> PropertyContains<TItem>(PropertyInfo propertyInfo, string value)
    {
        var param = Expression.Parameter(typeof(TItem));

        var m = Expression.MakeMemberAccess(param, propertyInfo);
        var c = Expression.Constant(value, typeof(string));
        var mi = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
        var body = Expression.Call(m, mi, c);

        return Expression.Lambda<Func<TItem, bool>>(body, param);
    }

Этот работает отлично. У меня также есть другой вспомогательный метод stati c для сравнения, если значения равны:

 public static Expression<Func<TItem, bool>> PropertyEquals<TItem, TValue>(
            PropertyInfo property, TValue value)
        {
            var param = Expression.Parameter(typeof(TItem));
            var xx = Expression.Property(param, property);
            var body = Expression.Equal(Expression.Property(param, property),
                Expression.Constant(value));
            return Expression.Lambda<Func<TItem, bool>>(body, param);
        }

Это также отлично работает.

НО, для десятичных полей (Decimal(19,4) в SQL) проблема, с которой я сталкиваюсь, заключается в том, что пользователь видит только 2 цифры как десятичное место в пользовательском интерфейсе, скажем, $ 19,32. Поэтому, когда они вводят 19,32 в поле поиска , он не совпадает с записью в БД, поскольку запись в БД на самом деле 19.3224, скажем так. Математически 19.32 != 19.3224, но, поскольку я показываю только 2 знака после запятой в пользовательском интерфейсе, он не возвращает искомый пользователь записи.

Моя проблема в том, что я не уверен, как манипулировать значением записи в базе данных в выражении. Или даже использования Decimal.Truncate пока будет достаточно (что означает, что все в порядке, если я игнорирую десятичные разряды и сравниваю только часть int)

Я попробовал что-то вроде ниже:

 public static Expression<Func<TItem, bool>> PropertyEqualMoneyFields<TItem>(PropertyInfo propertyInfo, decimal value) 
        {
            var param = Expression.Parameter(typeof(TItem));

            var m = Expression.MakeMemberAccess(param, propertyInfo);
            var c = Expression.Constant(value, typeof(decimal));
            var mi = typeof(decimal).GetMethod("Truncate", new Type[] { typeof(decimal) });
            var body = Expression.Call(m, mi, c);
            return Expression.Lambda<Func<TItem, bool>>(body, param); 
        }

и способ, которым я называю это как:

string filteredColumnNameInDb = "SomeMoneyColumnName"; // coming from ui actually
 PropertyInfo filteredProperty = typeof(SomeDto).GetProperty(filteredColumnNameInDb);
 query = query.Where(ExpressionHelper.PropertyEqualMoneyFields<SomeDto>filteredProperty,decimal.Truncate(decimalValue)));

, но он выдает исключение как Static method requires null instance, non-static method requires non-null instance., и, хотя не будет никакого исключения, это не сработает в любом случае, так как мне все еще нужно применить Expression.Equal в какой-то момент для сравнения, если они равны после усечения.

Если я полностью ухожу с дороги при таком подходе, я хотел бы услышать любой другой способ сделать это.

Или, если это неясно, было бы очень полезно, если бы кто-то мог укажите на «запуск функции sql для записи в БД перед выполнением какого-либо сравнения», поэтому, возможно, я бы создал некоторую функцию sql, чтобы сначала применить ее к этой записи, а затем выполните сравнение Expression.Equal.

PS: я знаю, что могу легко сделать это после ToList (), но в этом весь смысл фильтрации на стороне сервера, поскольку я НЕ хочу загружать весь набор данных!

...