LINQ Where忽略音调和大小写

32

使用LINQ的Where方法,忽略音调和大小写,最简单的过滤元素的方法是什么?

到目前为止,我已经能够通过在属性上调用方法来忽略大小写,但我认为这不是一个好主意,因为它会对每个元素调用相同的方法(是吗?)。

所以这是我到目前为止得到的:

var result = from p in People
             where p.Name.ToUpper().Contains(filter.ToUpper())
             select p;
请告诉我这是否是一个好的实践方法,以及忽略口音的最简单方式。
7个回答

61

为了忽略大小写和重音符号(变音符号),您可以先定义一个扩展方法,如下所示:

    public static string RemoveDiacritics(this String s)
    {
        String normalizedString = s.Normalize(NormalizationForm.FormD);
        StringBuilder stringBuilder = new StringBuilder();

        for (int i = 0; i < normalizedString.Length; i++)
        {
            Char c = normalizedString[i];
            if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
                stringBuilder.Append(c);
        }

        return stringBuilder.ToString();
    }

(修改自忽略带重音符号的字母进行字符串比较

现在您可以运行查询:

string queryText = filter.ToUpper().RemoveDiacritics();

var result = from p in People
         where p.Name.ToUpper().RemoveDiacritics() == queryText
         select p;

如果您只是在C#中遍历集合,则这很好,但如果您正在使用LINQ to SQL,则最好避免在LINQ查询中使用非标准方法(包括扩展方法)。这是因为您的代码无法转换为有效的SQL,从而无法在带有其所有可爱的性能优化的SQL Server上运行。

由于似乎没有一种标准方法可以在LINQ to SQL中忽略重音符号,在这种情况下,我建议将要搜索的字段类型更改为不区分大小写和重音符号(CI_AI)。

以您的示例为例:

ALTER TABLE People ALTER COLUMN Name [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AI

现在你的查询应该忽略音调和大小写。

请注意,在运行上述查询之前,您需要暂时删除字段上的任何唯一约束条件,例如:

ALTER TABLE People DROP CONSTRAINT UQ_People_Name

现在,您的LINQ查询将简单地是:

var result = from p in People
         where p.Name == filter
         select p;

请参见相关问题此处


1
太棒了。你介意我写博客分享吗? - Smur
1
很棒的回答。你在告诉真正的解决方案之前就回答了问题。太多人只做后者。 - Niels Brinch
5
注意,更改列的排序规则应该小心谨慎地进行。这可能会导致排序规则不匹配,并可能改变其他查询的语义。 - Frédéric

2

对于口音,如果您无法更新您的数据库模式或在RAM中获取整个列表,则可以枚举所有口音(这里是法语):

var result = from p in People
             where p.Name.ToLower()
                .Replace("à", "a")
                .Replace("â", "a")
                .Replace("ä", "a")
                .Replace("ç", "c")
                .Replace("é", "e")
                .Replace("è", "e")
                .Replace("ê", "e")
                .Replace("ë", "e")
                .Replace("î", "i")
                .Replace("ï", "i")
                .Replace("ô", "o")
                .Replace("ù", "u")
                .Replace("û", "u")
                .Replace("ü", "u").Contains(RemoveDiacritics(filter.ToLower()))
             select p;
                

1

更改排序规则:

ALTER TABLE dbo.MyTable 
ALTER COLUMN CharCol varchar(10)**COLLATE Latin1_General_CI_AS** NOT NULL;

0
如果您使用Linq-to-Entities,您可以:
1. 创建一个SQL函数来删除变音符号,例如将输入字符串应用于排序规则SQL_Latin1_General_CP1253_CI_AI。
CREATE FUNCTION [dbo].[RemoveDiacritics] (
@input varchar(max)
)   RETURNS varchar(max)

AS BEGIN
DECLARE @result VARCHAR(max);

select @result = @input collate SQL_Latin1_General_CP1253_CI_AI

return @result
END

2. 通过使用 DbFunction 属性将其映射到 DB 上下文(在本例中为 ApplicationDbContext)中,例如:

 public class ApplicationDbContext : IdentityDbContext<CustomIdentityUser>
    {
        [DbFunction("RemoveDiacritics", "dbo")]
        public static string RemoveDiacritics(string input)
        {
            throw new NotImplementedException("This method can only be used with LINQ.");
        }

        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
}

3. 在LINQ查询中使用它,例如:

var query = await db.Users.Where(a => ApplicationDbContext.RemoveDiacritics(a.Name).Contains(ApplicationDbContext.RemoveDiacritics(filter))).tolListAsync();

在编程中,过滤器是指你想要搜索的字符串,在这种情况下是数据库表Users的列名。


0
这里有一些代码,可以忽略重音进行比较: 忽略字符串比较中的重音字母 我会遵守规矩,不复制代码,这样作者就能得到回答的声望。现在,回答你的问题:
你可以获取那段代码,并像这样使用它:
var result = from p in People
             where p.Name.ToUpper().Contains(RemoveDiacritics(filter.ToUpper()))
             select p;

你甚至可以将那段代码转换成扩展方法。我已经做到了 :)


2
这个操作如何在 SQL 端去除值的重音?目前的情况下,它只会将大写的 SQL 值与无重音(大写)的 C# 值进行比较,是吗? - Kirk Woll
1
@Kirk - 你说得对,但我不确定是否可能在SQL端完成。我正要发布这个相同的解决方案。 - Justin Morgan
1
@Adrian,您向我展示的方法确实可以去除重音符号,但由于 LINQ 找不到到数据库的翻译,因此我无法在 SQL 方面调用它,正如 Kirk 和 Justin 所指出的那样。您们认为有没有办法使其在不触及数据库的情况下正常运行? - Smur
1
@Felipe,我相信直接使用Linq-To-Sql是不可能做到的。 但是,您可以编写一个存储过程(或TVF),将其他地方描述的排序建议集成。 然后,您可以在数据上下文中添加对此SP的方法调用 - Kirk Woll
1
@Kirk 我明白了,那我就需要直接在我的SQL Server数据库上创建一个'方法'。看起来很简单。 - Smur
1
这显然无法在 LINQ-to-SqlLINQ-to-Entities 中工作。 - QuantumHive

0

1
由于这不是一个新的答案,而是对已经被接受的答案的更新,您认为它需要发布为答案吗?在我看来,评论可能更合适。 - Eduard Malakhov
我声望还不到50,无法评论其他用户的帖子!否则我会很乐意的 :) - LePatay

0

从Entity Framework Core 5.0开始,您现在可以在Linq to SQL中动态更改查询的排序规则。

因此,对于您的示例,如果我想忽略大小写和重音符号,我会这样做:

(请注意,我们不能使用contains,但我们可以使用SQL的“like”运算符)

var result = from p in People
             where EF.Functions.Like(EF.Functions.Collate(p.Name, "Latin1_General_CI_AI"), $"%{filter}%")
             select p;

Latin1_General_CI_AI 是大小写不敏感(CI)和重音不敏感(AI)的。

有关 EF 排序规则和 EF 的大小写敏感性的更多信息,请参见此处:

https://learn.microsoft.com/en-us/ef/core/miscellaneous/collations-and-case-sensitivity#explicit-collation-in-a-query


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接