ASP.NET MVC 5中DataTable后端筛选的无重音匹配问题
解决LINQ to Entities中无法处理法语重音筛选的问题
这个问题我之前也碰到过,LINQ to Entities确实没办法直接把自定义的C#方法转换成数据库能理解的SQL语句,不过有几个靠谱的解决方案,咱们一个个来看:
方案一:利用数据库排序规则直接忽略重音(最简单)
大部分数据库都支持不区分重音的排序规则,比如SQL Server里的Latin1_General_CI_AI(CI=不区分大小写,AI=不区分重音)。你可以直接用EF的DbFunctions.Collate方法指定这个规则,这样筛选时就会自动忽略重音:
修改你的筛选逻辑代码:
else if (search != "") { textFilter = search.ToLower().RemoveDiacritics(); // 使用Collate指定不区分重音的排序规则 query = query.Where(x => DbFunctions.Collate(x.Nom.ToLower(), "Latin1_General_CI_AI").Contains(textFilter) || DbFunctions.Collate(x.Prenom.ToLower(), "Latin1_General_CI_AI").Contains(textFilter) || DbFunctions.Collate(x.Username.ToLower(), "Latin1_General_CI_AI").Contains(textFilter) || DbFunctions.Collate(x.Email.ToLower(), "Latin1_General_CI_AI").Contains(textFilter) || DbFunctions.Collate(x.EtabNom.ToLower(), "Latin1_General_CI_AI").Contains(textFilter) || DbFunctions.Collate(x.ActifStatut.ToLower(), "Latin1_General_CI_AI").Contains(textFilter) ); }
注意:如果你的数据库不是SQL Server,排序规则名称会不一样,比如MySQL可以用utf8_general_ci,需要根据你的数据库类型调整。
方案二:创建数据库自定义函数并映射到EF
如果方案一的排序规则不能满足你的需求(比如需要处理更多特殊字符),可以在数据库中创建一个去重音的标量函数,然后在EF中映射这个函数,这样LINQ就能调用它了。
第一步:在数据库中创建函数
执行以下SQL语句(以SQL Server为例):
CREATE FUNCTION [dbo].[RemoveDiacritics] ( @input NVARCHAR(MAX) ) RETURNS NVARCHAR(MAX) AS BEGIN DECLARE @output NVARCHAR(MAX) = @input -- 替换常见法语重音字符 SET @output = REPLACE(@output, N'é', N'e') SET @output = REPLACE(@output, N'è', N'e') SET @output = REPLACE(@output, N'ê', N'e') SET @output = REPLACE(@output, N'ë', N'e') SET @output = REPLACE(@output, N'à', N'a') SET @output = REPLACE(@output, N'â', N'a') SET @output = REPLACE(@output, N'ä', N'a') SET @output = REPLACE(@output, N'ô', N'o') SET @output = REPLACE(@output, N'ö', N'o') SET @output = REPLACE(@output, N'ù', N'u') SET @output = REPLACE(@output, N'û', N'u') SET @output = REPLACE(@output, N'ü', N'u') SET @output = REPLACE(@output, N'ç', N'c') -- 可以继续添加其他需要处理的字符 RETURN @output END
第二步:在EF的DbContext中映射函数
在你的DbContext类中添加这个静态方法:
[DbFunction("dbo", "RemoveDiacritics")] // 第一个参数是你的数据库架构名,默认是dbo public static string RemoveDiacritics(string input) { // 这个方法只会在LINQ to Entities中被转换为SQL调用,不会直接执行 throw new NotSupportedException("This method is only supported in LINQ to Entities queries."); }
第三步:在筛选逻辑中使用
现在你就可以在LINQ查询中调用这个方法了:
else if (search != "") { textFilter = search.ToLower().RemoveDiacritics(); query = query.Where(x => DbContext.RemoveDiacritics(x.Nom.ToLower()).Contains(textFilter) || DbContext.RemoveDiacritics(x.Prenom.ToLower()).Contains(textFilter) || // 其他字段同理 ); }
方案三:内存中筛选(适合小数据量)
如果你的数据量不大,可以先把基础查询的结果加载到内存,再用你的自定义RemoveDiacritics方法筛选。但要注意,这个方法会把所有符合基础条件的数据都拉到内存里,数据量大的话会影响性能:
else if (search != "") { textFilter = search.ToLower().RemoveDiacritics(); // 先加载到内存 var data = query.ToList(); // 内存中筛选 data = data.Where(x => x.Nom.ToLower().RemoveDiacritics().Contains(textFilter) || x.Prenom.ToLower().RemoveDiacritics().Contains(textFilter) || // 其他字段同理 ).ToList(); // 如果需要继续用IQueryable,可以转回去,但此时已经是内存数据了 query = data.AsQueryable(); }
总结
- 优先推荐方案一,操作最简单,不需要额外代码,利用数据库原生功能性能也最好。
- 如果排序规则不能满足需求,选择方案二,自定义函数更灵活可控。
- 只有数据量很小的时候才考虑方案三,避免性能问题。
内容的提问来源于stack exchange,提问作者FrankLar21
相关产品推荐
相关产品推荐

