Динамическое создание выражений LINQ — ToLower и «Содержит» одновременно

Я пытаюсь добавить ToLower() в выражения «Содержит LINQ»: Вот мой код с динамическими выражениями LINQ (содержит):

private static Expression GetExpressionCase<T>(ParameterExpression param, SearchCriteria searchCriteria)
{
    MethodInfo containsMethod =
        typeof(string).GetMethod("Contains", new[] { typeof(string) });

    MemberExpression member =
        Expression.Property(param, searchCriteria.Key);

    ConstantExpression constant =
        Expression.Constant(Convert.ChangeType(searchCriteria.Value, member.Type));

    switch (searchCriteria.Operation)
    {
        case '=':
            return Expression.Equal(member, constant);

        case '>':
            return Expression.GreaterThanOrEqual(member, constant);

        case '<':
            return Expression.LessThanOrEqual(member, constant);

        case ':':
            return Expression.Call(member, containsMethod, constant);
     }

     return null;
}

Мои коды работают нормально, но я хочу добавить ToLower() перед contains(), как этот запрос:

Текущий запрос выглядит следующим образом (только Contains()):

var test = context.Table.Where(x => x.Key.Contains("value"));

Я надеюсь, что запрос будет выглядеть следующим образом (добавлен ToLower()):

var test = context.Table.Where(x => x.Key.ToLower().Contains("hoa"));

Я уже пробовал использовать StringComparison.OrdinalIgnoreCase, но он не может быть переведен поставщиком запросов (postgreSQL).

SQL-сравнения по умолчанию уже нечувствительны к регистру. И даже если у вас есть нестандартные параметры сортировки с учетом регистра, ToLower() НЕ является правильным способом выполнить сравнение без учета регистра.

Joel Coehoorn 13.08.2024 16:28

почему вы создаете выражения, а не просто пишете код? возможно, это часть чего-то большего, но вы можете условно связывать вещи с IQueryable на основе ваших критериев поиска.

Krzysztof Skowronek 13.08.2024 16:35

@JoelCoehoorn В моем случае postgreSQL не выполняет сравнения без учета регистра. Я знаю, что в Postgre есть ILike, если я прямо запишу запрос, используемый ILike, он работает нормально, но с Contain («ключевое слово», StringComparison.OrdinalIgnoreCase) EF не может его перевести.

Son Bui 14.08.2024 05:06
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
60
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

fi => fi.DESCRIPTION.ToLower().Contains(description.ToLower())

К сожалению, ОП просит найти способ построить дерево выражений, которое будет представлять написанный вами код, а не сам код.

AgentFire 13.08.2024 16:06

Также не публикуйте только код ответов. объясните, как работает код и почему он решает проблему ОП.

Mark Baijens 13.08.2024 16:29

У вас это хорошо получается, я бы только предложил просто связать результат вызова ToLower с Contains.

case ':':
    MethodInfo containsMethod =
        typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string) });

    MethodInfo toLowerMethod =
        typeof(string).GetMethod(nameof(string.ToLower), Type.EmptyTypes);

    MethodCallExpression toLowerCall = // X.ToLower();
        Expression.Call(member, toLowerMethod, constant);

    MethodCallExpression containsCall = // X.Contains(Y);
        Expression.Call(toLowerCall, containsMethod, constant);

    return containsCall;

Спасибо за ваш ответ, это умный подход, о котором я никогда не думал. К сожалению, он выдает exc: Incorrect number of arguments supplied for call to method 'System.String ToLower()' (Parameter 'method') когда toLowerCall X.ToLower()

Son Bui 14.08.2024 06:42

конечно, потому что ToLower не имеет параметров. Поэтому вместо того, чтобы передавать constant в качестве последнего параметра, просто опустите его.

MakePeaceGreatAgain 14.08.2024 07:34
Ответ принят как подходящий

Попробуйте следующую реализацию. Я немного упростил это.

private static Expression? GetExpressionCase(ParameterExpression param, SearchCriteria searchCriteria, bool makeLower)
{
    Expression memberExpression = Expression.Property(param, searchCriteria.Key);

    if (makeLower && memberExpression.Type == typeof(string))
    {
        memberExpression = Expression.Call(memberExpression, nameof(string.ToLower), Type.EmptyTypes, memberExpression);
    }

    var searchValue = searchCriteria.Value;
    if (makeLower && searchValue is string str)
    {
        searchValue = str.ToLower();
    }

    var constant = Expression.Constant(Convert.ChangeType(searchValue, memberExpression.Type));

    switch (searchCriteria.Operation)
    {
        case '=':
            return Expression.Equal(memberExpression, constant);

        case '>':
            return Expression.GreaterThanOrEqual(memberExpression, constant);

        case '<':
            return Expression.LessThanOrEqual(memberExpression, constant);

        case ':':
            return Expression.Call(memberExpression, nameof(string.Contains), Type.EmptyTypes, constant);

        default:
            return null;
    }
}

Спасибо, это хорошо работает. Дополнительно: я изменил memberExpression = Expression.Call(memberExpression, nameof(string.ToLower), Type.EmptyTypes, memberExpression); на memberExpression = Expression.Call(memberExpression, nameof(string.ToLower), Type.EmptyTypes, null);, чтобы избежать возникновения исключения - неправильное количество аргументов, предоставленных для вызова метода.

Son Bui 14.08.2024 06:38

Другие вопросы по теме