我需要在具有唯一索引的字符串列的表中实现导入功能。为了访问数据库,我使用 EF Core 8。为了确保导入顺利运行,我删除了 C# 代码中的所有重复项,但导入仍然失败。
我进行了深入研究,发现了一个应该与 SQL Server 的行为相匹配的比较器:来自
CollationInfo.GetCollationInfo("Latin1_General_CI_AS").EqualityComparer
包的 Microsoft.SqlServer.SqlManagementObjects
。但当我比较时:
CollationInfo.GetCollationInfo("Latin1_General_CI_AS").EqualityComparer.Equals("ß","SS")
我在尝试将此字符串导入数据库时遇到
false
,由于唯一约束,将会失败。
首先通过 EF 代码创建数据库。我调用
modelBuilder.UseCollation("Latin1_General_CI_AS");
以确保使用相同的排序规则,当我检查唯一索引时,它具有此排序规则集(以及数据库默认排序规则)。
有没有完全匹配 SQL 排序规则的 EqualityComparer?
由于您使用的是 EF8,您的目标框架必须是 .NET 8。根据这个问题,.NET 5 在字符串比较算法中引入了重大更改,您正在使用它的较新版本(基于 ICU)。
即使
ß
和 ss
被认为是相等的,因为 ß
仅在德语中使用,ICU 对 ß
和 ss
比较的处理也不同。坦白说,我并不完全理解这个解释,但我想这是有充分理由的。
就我而言,在最新 Windows 10 上运行的 .NET 6 应用程序每次比较都会返回
false
:
var k1 = StringComparer.Create(new CultureInfo("en-US", false), CompareOptions.IgnoreCase | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType).Equals("ß", "SS");
var k2 = StringComparer.Create(new CultureInfo("de-DE", false), CompareOptions.IgnoreCase | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType).Equals("ß", "SS");
var k21 = StringComparer.Create(new CultureInfo("de-AT", false), CompareOptions.IgnoreCase | CompareOptions.IgnoreWidth | CompareOptions.IgnoreKanaType).Equals("ß", "SS");
除非我按照建议添加
CompareOptions.IgnoreNonSpace
。
您的 .NET 代码不起作用,因为
CollationInfo.GetCollationInfo("Latin1_General_CI_AS")
返回的比较器是在没有这个新的 CompareOptions.IgnoreNonSpace
解决方法的情况下创建的(顺便说一句,您使用的是旧版本的包,对吧?尝试更新到最新的 v170+)。
现在回到 MS SQL Server。我找不到 SQL Server 字符串比较在幕后如何工作的解释,但我认为它必须使用操作系统的函数才能获得最佳性能,或者尽可能模仿默认的 Windows 行为(即旧的基于 NLS 的行为)即使操作系统升级,也可以提供相同的一致结果。无论哪种方式,它的工作方式都与最新的 .NET 实现不同。