实体框架核心 - 包含区分大小写还是不区分大小写?
Posted
技术标签:
【中文标题】实体框架核心 - 包含区分大小写还是不区分大小写?【英文标题】:Entity Framework core - Contains is case sensitive or case insensitive? 【发布时间】:2017-09-02 19:45:35 【问题描述】:Entity Framework 核心中的“包含”应该等同于 SQL %like% 运算符。因此“包含”应该不区分大小写,但它是区分大小写的! (至少在 postgres 中????)
以下仅在使用正确的关键字大小写时输出结果。
context.Counties.Where(x => x.Name.Contains(keyword)).ToList();
我做错了什么?
【问题讨论】:
LIKE 在 postgresql 中区分大小写。 请注意,有一个名为“citext”的扩展名,它允许您使用不区分大小写的“citext”类型的列进行比较。否则,您应该显式使用lower
来执行不区分大小写的比较。 postgresql 中还有ILIKE
,它是LIKE
的不区分大小写版本。
@Evk 难怪!
【参考方案1】:
以前是旧版本的 EF 内核的情况。现在string.Contains
区分大小写,例如 sqlite 它映射到 sqlite 函数 `instr()' (我不知道对于 postgresql)。
如果您想以不区分大小写的方式比较字符串,您可以使用 DbFunctions 来完成这项工作。
context.Counties.Where(x => EF.Functions.Like(x.Name, $"%keyword%")).ToList();
@Gert 更新:
问题中的部分假设不正确。 string.Contains
不会转换为 LIKE expression
,即使它曾经在 ef 核心版本
string.contains
转换为CHARINDEX()
,在oracle 和sqlite 转换为instr()
默认情况下区分大小写,除非另外定义了db 或列排序规则(同样,我不知道对于 postgresql )。
在所有情况下,EF.Functions.Like()
都会转换为默认情况下不区分大小写的 SQL LIKE
表达式,除非另外定义了 db 或列排序规则。
所以是的,这一切都归结为排序规则,但是 - 如果我错了,请纠正我 - 在某种程度上,代码可能会影响区分大小写/不区分大小写的搜索,具体取决于您使用上述哪种方法。
现在,我可能不是完全最新的,但我认为 EF 核心迁移不会自然地处理数据库排序规则,除非您已经手动创建了表,否则您最终会使用默认排序规则(区分大小写sqlite,我真的不知道其他人)。
回到最初的问题,如果在未来的版本中没有 3 个选项,您至少有 2 个选项来执行此不区分大小写的搜索:
-
使用此trick 在创建时使用 DbContext.OnModelCreating() 指定列排序规则
将您的
string.Contains
替换为EF.Functions.Like()
或等待discussion 中仍有前景的功能:EF.Functions.Collate()
函数
【讨论】:
不正确。它仅取决于数据库排序规则。 EF在这里没有任何影响,更不用说EF版本了。 排序规则对此有影响,我不是说别的。但是,如果您不想使用排序规则,您可以选择我在这里指出的选项。但是,让我编辑我的回复,希望我不会错。 好的,继续,但是除了申请ToLower
之外,真的没有办法从客户那里得到指导,但那是“作弊”。
EF.Functions.ILike
... 发现这一点真是太棒了。谢谢。
安全性怎么样;这似乎很可能不安全:EF.Functions.Like(x.Name, $"%keyword%")【参考方案2】:
我的回答将涉及 NpgSQL。
PostgreSQL 中的EF.Functions.Like()
区分大小写,但您可以使用位于Npgsql.EntityFrameworkCore.PostgreSQL
assembly 的EF.Functions.ILike()
扩展method。
如果您在构建查询的地方没有参考实体框架程序集,您可以使用ToLower()
和Contains()
组合方法,因为Npgsql 是able 转换ToLower()
方法来纠正SQL
例子:
context.Counties.Where(x => x.Name.ToLower().Contains(keyword.ToLower())).ToList();
关于第二种方法请记住:您可能会遇到性能问题并且可能会遇到与编码相关的问题。
【讨论】:
性能地狱正是把我带到这里的原因,我使用的是第二种方法,我无法理解为什么我的查询需要 5 分钟才能执行,请小心使用第二种方法,性能上的变化是吓人的。 我在这里参加聚会有点晚了,但我想知道性能影响,因为第二种方法似乎对我很有效。我检查了查询,发现它使用strpos(lower(...))
进行字符串比较,在这篇文章 (dba.stackexchange.com/questions/89901/…) 中提到它是更快的选择。我在这里错过了什么吗?
@HakanFıstık ToLower in postgres 将使用扫描列,这就是它性能非常差的原因。【参考方案3】:
IQueryable.Where
在数据库中执行,所以很可能不区分大小写。
IEnumerable.Where
使用 C# String.Contains
,所以区分大小写。
阅读此答案:Returning IEnumerable vs. IQueryable
【讨论】:
IQueriable 的行为将取决于数据库。例如,在 SQL Server 中,它不区分大小写,但对于 postgres,它是区分大小写的【参考方案4】:试试吧:
您可以Lower case
字段和搜索值
context.Counties.Where(x => x.Name.ToLower().Contains(keyword.ToLower())).ToList();
或者你可以Upper Case
提交并搜索值
context.Counties.Where(x => x.Name.ToUpper().Contains(keyword.ToUpper())).ToList();
【讨论】:
这可能会导致非常大的性能问题,尤其是对于 PostgreSQL,请参阅上面的@Stas 答案以避免您可能面临的性能灾难 还有不变文化的问题,你不能通过不变文化【参考方案5】:在查询中使用显式排序规则
例如
var customers = context.Customers
.Where(c => EF.Functions.Collate(c.Name, "SQL_Latin1_General_CP1_CS_AS") == "John")
.ToList();
更多详情见msdn链接
https://docs.microsoft.com/en-us/ef/core/miscellaneous/collations-and-case-sensitivity
【讨论】:
【参考方案6】:使用 Entity Framework Core 3.1 和 mysql / MariaDB 提供程序,您可以通过以下方式手动设置大小写(不)敏感度 StringComparison.InvariantCultureIgnoreCase
:
items = items.Where(i =>
i.Name.Contains(value, StringComparison.InvariantCultureIgnoreCase));
默认行为似乎区分大小写,但是您可以使用 StringComparison.InvariantCulture
显式设置它。
有关更多信息,请查看我博客上的this post。
我不知道它是否也适用于以前的版本(将相应地检查并更新此答案)。
【讨论】:
这款的表现如何?它是否被转化为使用 MySQL 的高效查询?还是必须逐个加载条目才能在服务器上进行比较? 翻译成这样:WHERE (LOCATE(CONVERT(LCASE(@__value_0) USING utf8mb4) COLLATE utf8mb4_bin, LCASE(b.Name)) > 0)
以上是关于实体框架核心 - 包含区分大小写还是不区分大小写?的主要内容,如果未能解决你的问题,请参考以下文章