Выражение LINQ не переводится в SQL

Я пытаюсь выполнить скомпилированный SQL-запрос из LINQ, который будет проверять, является ли query подстрокой любого из трех столбцов в базе данных (без учета регистра).

Я использую .NET Core 1.1

Запрос, который я придумал, выглядит следующим образом:

users.Select(u => new
  {
    User = u,
    query = u.FirstName.ToLower() + u.LastName.ToLower() + u.Email.ToLower()
  }).Where(x => x.query.Contains(query))

Но при просмотре информации об отладке я получаю следующее предупреждение:

Выражение LINQ '(([u] .FirstName.ToLower () + [u] .LastName.ToLower ()) + [u] .Email.ToLower () ». Contains (__ query_0)' не может быть переведено и будет оценивается на местном уровне.

Второй запрос, который я пробовал:

 users.Where(x => u.FirstName.ToLower().Contains(query) || u.LastName.ToLower().Contains(query) || u.Email.ToLower().Contains(query))

но он дает мне точно такое же предупреждение.

Почему так? Я ищу что-то вроде:

SELECT * FROM USERS WHERE FirstName LIKE query OR LastName LIKE query OR Email LIKE query

ОБНОВЛЕНИЕ

Провел еще один эксперимент:

    users.Where(u =>
    u.FirstName.Contains(query) ||
    u.LastName.Contains(query) ||
    u.Email.Contains(query));

И это тоже привело к

Выражение LINQ '(([u] .FirstName.Contains (__ query_0) OrElse [u] .LastName.Contains (__ query_1)) OrElse [u] .Email.Contains (__ query_2) »не может быть переведено и будет оцениваться локально. .


person MaLiN2223    schedule 26.10.2017    source источник
comment
Если параметры сортировки вашей базы данных xxx_CI, .ToLower(). SELECT CONVERT (varchar, SERVERPROPERTY('collation')); для проверки.   -  person Magnus    schedule 26.10.2017
comment
Спасибо за подсказку, у меня SQL_Latin1_General_CP1_CI_AS. Однако я думаю (возможно, я ошибаюсь), что когда мой запрос не скомпилирован в SQL, он будет чувствителен к регистру.   -  person MaLiN2223    schedule 26.10.2017
comment
Я не уверен, что вы имеете в виду под не скомпилированным? Если вы запустите тот же запрос для Linq2Objects, чем да, он будет действовать по-другому. (Деликатный случай)   -  person Magnus    schedule 26.10.2017
comment
Обратите внимание, что ваш метод запросов имеет недостатки (которые могут вас волновать, а могут и нет). Для пользователя по имени Джон Найман вы создадите запрос типа [email protected]. Если пользователь ищет Джонни, он получит доступ к Джону Найману, даже если Джонни не встречается ни в имени, ни в фамилии, ни в адресе электронной почты. Также обратите внимание, что вы получите разные результаты, если поменяете порядок конкатенации: указание фамилии первым ([email protected]) внезапно не даст результатов для Джонни.   -  person Flater    schedule 26.10.2017
comment
@Flater это правда! Спасибо, меня это волнует.   -  person MaLiN2223    schedule 26.10.2017


Ответы (1)


Это связано с тем, что .ToLower() и .Contains() являются функциями в строковом классе и не могут быть переведены в SQL поставщиком linq.
Все запросы (если явно не указаны) будут следовать параметрам сортировки базы данных, а если это CI, это нечувствительность к регистру, и вы не нужен .ToLower(). Что касается .Contains(), вам нужно использовать функцию сущности Like.

users.Where(u =>
    EF.Functions.Like(u.FirstName, "%" + query + "%") ||
    EF.Functions.Like(u.LastName, "%" + query + "%") ||
    EF.Functions.Like(u.Email, "%" + query + "%"));

Однако, похоже, это добавлено в EF core 2.0. Для 1.1 я не думаю, что есть какой-либо способ сделать это. Я бы рекомендовал пропустить EF и написать старый простой SQL напрямую.

person Magnus    schedule 26.10.2017
comment
Верно ли это, даже если часть запроса оценивается локально? - person MaLiN2223; 26.10.2017
comment
Нет, часть linq2Object будет учитывать регистр. Я бы посоветовал вам сделать .AsEnumerable(), чтобы указать, что некоторая часть запроса должна быть локальной. - person Magnus; 26.10.2017
comment
Я этого недооцениваю. Однако это не отвечает на мой первоначальный вопрос: почему указанный выше запрос LINQ не переводится на SQL. - person MaLiN2223; 26.10.2017
comment
Это потому, что провайдер linq не может переводить .ToLower(), поскольку это функция в строковом классе. - person Magnus; 26.10.2017
comment
Не могу перевести .Contains(), вам нужно использовать функцию сущности Like. - person Magnus; 26.10.2017
comment
Есть ли EF.Functions в ядре EF для ядра dotnet 1.1? Если да, что мне нужно включить в свой проект? - person MaLiN2223; 26.10.2017
comment
Я думаю, что это было добавлено в Core 2.0. В EF Core отсутствует множество функций по сравнению с EF 6. См. blogs.msmvps.com/ricardoperes/2016/09/05/, чтобы узнать подробнее. - person Magnus; 26.10.2017
comment
Спасибо за помощь, поставлю +1 на ваш ответ. Может у кого-то есть метод для 1.1 - person MaLiN2223; 26.10.2017
comment
Как оказалось, в версии 1.1 это невозможно. Я перенес свой проект на версию 2 и использовал вашу логику. - person MaLiN2223; 01.02.2018