执行以下代码时,出现错误:
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)
方法。
我该如何解决?
任何评论将不胜感激。
答案 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()
)。