为什么“ LINQ表达式'x'无法翻译”?我没有使用“ Where()”

时间:2020-03-05 21:24:09

标签: c# linq entity-framework-core

执行以下代码时,出现错误:

System.InvalidOperationException:LINQ表达式'DbSet.Where(u => u.NormalizedEmail == __ToLower_0 && u.PasswordHash.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 个答案:

答案 0 :(得分:4)

您可能在“受限客户评估”中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));

答案 1 :(得分:4)

您说它在dotnet core 2.2中有效,但在dotnet core 3.1中无效。我假设这意味着您还在Entity Framework Core 3版本中使用dotnet core 3.1,这似乎是Entity Framework Core 3中的一项重大更改。 See here

旧行为

在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 :(得分:2)

在3.0之前,EF Core无法翻译的表达式将导致所有内容都被拉入客户端,然后在客户端进行过滤,这可能会变得非常慢。在3.0中,EF Core现在会引发一个异常,该异常旨在使开发人员优化其查询。

因此,您的错误意味着EF Core无法将u.PasswordHash.SequenceEqual(pass)转换为SQL。您应该找到另一种过滤查询的方法。

EF Core可以将有限的方法转换为SQL(例如string.StartsWith()list.Contains())。