Почему «LINQ выражение« x »не может быть переведено»? Я не использую "Где ()" - PullRequest
4 голосов
/ 06 марта 2020

Когда я выполняю следующий код, я получаю сообщение об ошибке:

System.InvalidOperationException: выражение LINQ 'DbSet.Where (u => u.NormalizedEmail == __ToLower_0 && u.PasswordHa sh .SequenceEqual (__ pass_1)) 'не может быть переведен

Но он работал нормально в. NET Core 2.2. Ошибка возникает в. NET Core 3.1.

loginCreds.Password = loginCreds.Password.ToLower();
var pass = Helper.ComputeHash(loginCreds.Password);
var usr = await _context.Users
                        .FirstOrDefaultAsync(u => u.NormalizedEmail == loginCreds.Email
                                                  && u.PasswordHash.SequenceEqual(pass));

Но, когда я заменяю u.PasswordHash.SequenceEqual(pass) на u.PasswordHash == new byte[16] (только для тестирования), это работает. Итак, проблема в методе SequenceEqual(byte[] byte).

Как я могу решить эту проблему?

Любой комментарий будет оценен.

Ответы [ 3 ]

4 голосов
/ 06 марта 2020

Ответ может быть здесь EF Core 3.0 в разделе «Оценка ограниченного клиента»

Например, если EF Core 2.2 не может перевести предикат в Where (), он выполнил оператор SQL без фильтра, перенес все строки из базы данных, а затем отфильтровал их в памяти

....

В EF Core 3.0 мы ограничили оценку клиента только для проекции верхнего уровня (по сути, последнего вызова Select ()). Когда EF Core 3.0 обнаруживает выражения, которые не могут быть переведены где-либо еще в запросе, он выдает исключение времени выполнения.

В EF Core 2.2 часть запроса с SequenceEqual фактически не была выполнена в SQL.

Вы должны попытаться сделать это:

var usr = await _context.Users
.Where(u => u.NormalizedEmail == loginCreds.Email)
.ToListAsync()
.FirstOrDefault(u => u.PasswordHash.SequenceEqual(pass));
4 голосов
/ 06 марта 2020

Вы сказали, что это работает в dotnet core 2.2, но не в dotnet core 3.1. Я предполагаю, что это означает, что вы также используете Entity Framework Core 3 в версии dotnet core 3.1, и, похоже, это серьезное изменение в Entity Framework Core 3. См. Здесь .

Старое поведение

До версии 3.0, когда EF Core не мог преобразовать выражение, являющееся частью запроса, в SQL или параметр, он автоматически оценивает выражение на клиенте. По умолчанию оценка клиентом потенциально дорогих выражений только вызвала предупреждение.

Новое поведение

Начиная с 3.0, EF Core допускает только выражения в проекции верхнего уровня (последний вызов Select () в запросе) для оценки на клиенте. Если выражения в любой другой части запроса не могут быть преобразованы либо в SQL, либо в параметр, возникает исключение.

SequenceEqual нельзя преобразовать в SQL или параметр так что в версии 2.2 это автоматически выполнялось на клиенте. Теперь в версии 3.1 выдается InvalidOperationException. Использование оператора равенства работает, потому что это можно перевести в оператор SQL.

Чтобы исправить это, почему бы не выбрать Email, а затем сравнить пароль?

loginCreds.Password = loginCreds.Password.ToLower();
var pass = Helper.ComputeHash(loginCreds.Password);
var usr = await _context.Users
    .FirstOrDefaultAsync(u =>u.NormalizedEmail == loginCreds.Email));
bool validUser = false;
if (usr != null)
{
    validUser = usr.PasswordHash.SequenceEquals(pass);
}
// if validUser is true, then the credentials were valid.
2 голосов
/ 06 марта 2020

До версии 3.0 выражения, которые EF Core не мог перевести, приводили к тому, что все загружалось в клиент, а затем фильтровалось там, что могло стать очень медленным. В версии 3.0 EF Core теперь создает исключение, предназначенное для разработчиков, чтобы оптимизировать свои запросы.

Итак, ваша ошибка означает, что EF Core не может перевести u.PasswordHash.SequenceEqual(pass) в SQL. Вы должны найти другой способ фильтрации вашего запроса.

Существует ограниченный набор методов, которые EF Core переводит в SQL (например, string.StartsWith(), list.Contains()).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...