Alv*_*mli 5 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));
Run Code Online (Sandbox Code Playgroud)
但是,当我替换u.PasswordHash.SequenceEqual(pass)为u.PasswordHash == new byte[16](仅用于测试)时,它起作用了。所以,问题是SequenceEqual(byte[] byte)方法。
我该如何解决这个问题?
小智 6
您可能会在“受限客户端评估”中的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));
Run Code Online (Sandbox Code Playgroud)
你说它在 中有效,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.
Run Code Online (Sandbox Code Playgroud)