【问题标题】:Net core EF 3.1 LINQ string comparison no longer workingNet core EF 3.1 LINQ 字符串比较不再起作用
【发布时间】:2020-05-04 20:13:17
【问题描述】:

我有以下课程:

public class Employee
{
    public string Name {get; set;}
    ...
}

以及 EF Core 2.1 中的 LINQ 查询

Employee GetEmployeeByName(string name) {
  return Context.Employee.Where ( w =>String.Compare(w.Name, name, true) == 0).FirstOrDefault();
}

转成Net Core EF 3.1后出现错误。

无法翻译 LINQ 表达式。以可翻译的形式重写查询,或通过插入对AsEnumerable()AsAsyncEnumerable()ToList()ToListAsync() 的调用显式切换到客户端评估

我必须将查询更改为

Employee GetEmployeeByName(string name) {
  return Context.Employee.Where ( w =>w.Name.ToLower() == name.ToLower()).FirstOrDefault();
}

有没有更好的方法来做到这一点?

【问题讨论】:

  • 你用的是什么数据库?
  • Microsoft SQL Server。
  • 我正朝着 barett-string ops 的答案前进,如果它们是在客户端 c# 上完成的,它们只会发送案例。对于传递给数据库的简单查询,例如.Where(x => x.Name == "Smith"),case sens 来自 db,默认情况下 SQLS 通常不是 case sens

标签: c# linq entity-framework-core ef-core-3.1


【解决方案1】:

如果您要进行不区分大小写的字符串比较,建议 (AFAIK) 是使用 EF.Functions 扩展,它会转换为正确的 SQL 语句。

你的例子变成了这个(使用Like):

using Microsoft.EntityFrameworkCore;

Employee GetEmployeeByName(string name) {
  return Context.Employee.Where(w => EF.Functions.Like(w.Name, name)).FirstOrDefault();
}

翻译成类似的东西(取决于服务器版本)

SELECT TOP(1) <<list of fields here>> FROM Employee WHERE Name LIKE(@name)

functions which are available 取决于 EF Core 和底层 DBMS 的版本,但由于您提到了 SQL Server,假设您使用“默认”排序规则,上述内容将起作用。相关:Is the LIKE operator case-sensitive with MSSQL Server?

【讨论】:

  • is EF.Functions 可以与除 MsSql 之外的其他 DB-Engine 一起使用吗?
  • 是的。哪些功能可用,哪些有效,取决于您安装的提供程序。不过,这在我的回答中有所说明。
  • 没有。 EF.Functions 类可用于多个供应商。你只是不会看到相同的功能,这取决于你使用的是什么。
  • 嗯,我明白你的意思。我很抱歉我的英语不好,也许我之前的评论对你来说不清楚。非常感谢您的精彩解释。
【解决方案2】:

正如@tieson-t 提到的,您可以使用 EF.Functions 进行 Like 比较,但也许更“正确”的方式(当然取决于用例)是指定比较的排序规则。

不是像@barrett777 建议的那样定义列的默认排序规则,而是有一个名为 Collat​​e 的 EF.Function,您可以在其中指定用于当前查询的排序规则。

以我使用的 SQLite 为例

.Where(t => EF.Functions.Collate(t.Name, "NOCASE").Equals(m))

更多排序规则信息...

【讨论】:

    【解决方案3】:

    我相信 EF Core 查询的大小写敏感性来自数据库中配置的排序规则

    https://docs.microsoft.com/en-us/sql/relational-databases/collations/collation-and-unicode-support?view=sql-server-ver15

    您可以将 Employee.Name 列的排序规则设置为不区分大小写的排序规则,然后您对该列的 EF Core 查询默认情况下应该不区分大小写

    【讨论】:

      【解决方案4】:

      您仍然可以做同样的事情...在 .NET Core 2.2 中执行此操作

      Employee GetEmployeeByName(string name) {
          return Context.Employee.Where(w =>String.Compare(w.Name, name, true) == 0).FirstOrDefault();
      }
      

      在 .NET Core 3.1 中确实是同样的事情:

      Employee GetEmployeeByName(string name) {
          var entityList = Context.Employee.ToList();
          return entityList.Where(w =>String.Compare(w.Name, name, true) == 0).FirstOrDefault();
      }
      

      这只是 Microsoft 让您意识到您必须检索表的所有条目才能进行字符串比较的一种方式......就像在 .NET Core 2.2 中发生的那样。

      来源:https://docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.0/breaking-changes#linq-queries-are-no-longer-evaluated-on-the-client

      【讨论】:

      • 这是一种低效的方式,它将您在 DB-Server 上的所有数据加载到 App-Server 中,然后按特定条件过滤结果(并删除不匹配的结果)。您必须保留 IQueryable&lt;T&gt; 中的行以确保 EF 加载(生成查询)仅过滤数据。
      • 当然是,这就是微软决定让它成为错误的原因,除非你是故意的。由于“String.Compare”不会转换为 SQL 查询,因此没有其他方法可以准确地完成所要求的操作。
      • 在那种情况下,我同意了。但是对于 LinqToSql,使用 ToLower 是最好的方法(我认为)。
      • 当然,如果不区分大小写的话。
      猜你喜欢
      • 2011-09-20
      • 2021-05-11
      • 2014-04-03
      • 2012-01-22
      • 2017-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多