- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我在我的数据库和我的 C# 代码之间实现了一个缓存层。这个想法是根据查询的参数缓存某些数据库查询的结果。数据库使用默认排序规则 - SQL_Latin1_General_CP1_CI_AS
或 Latin1_General_CI_AS
,我相信基于一些简短的谷歌搜索,这等同于相等,只是排序不同。
我需要一个 .NET StringComparer,它可以为我提供相同的行为,至少对于相等性测试和哈希码生成,就像数据库的排序规则正在使用的那样。目标是能够在 C# 代码中的 .NET 字典中使用 StringComparer 来确定特定字符串键是否已在缓存中。
一个真正简化的例子:
var comparer = StringComparer.??? // What goes here?
private static Dictionary<string, MyObject> cache =
new Dictionary<string, MyObject>(comparer);
public static MyObject GetObject(string key) {
if (cache.ContainsKey(key)) {
return cache[key].Clone();
} else {
// invoke SQL "select * from mytable where mykey = @mykey"
// with parameter @mykey set to key
MyObject result = // object constructed from the sql result
cache[key] = result;
return result.Clone();
}
}
public static void SaveObject(string key, MyObject obj) {
// invoke SQL "update mytable set ... where mykey = @mykey" etc
cache[key] = obj.Clone();
}
StringComparer 与数据库的排序规则匹配很重要的原因是误报和漏报都会对代码产生不良影响。
如果 StringComparer 说两个键 A 和 B 相等,而数据库认为它们是不同的,那么数据库中可能有两行具有这两个键,但如果被询问,缓存将阻止返回第二个键连续获取 A 和 B - 因为 B 的获取将错误地命中缓存并返回为 A 检索到的对象。
如果 StringComparer 说 A 和 B 不同,而数据库认为它们相等,那么问题就更微妙了,但同样有问题。对两个键的 GetObject 调用都可以,并返回对应于同一数据库行的对象。但是随后使用键 A 调用 SaveObject 会使缓存不正确;仍然会有一个包含旧数据的 key B 的缓存条目。随后的 GetObject(B) 将提供过时的信息。
因此,为了让我的代码正常工作,我需要 StringComparer 来匹配数据库行为以进行相等性测试和哈希码生成。到目前为止,我的谷歌搜索已经产生了很多关于 SQL 排序规则和 .NET 比较并不完全等同的信息,但没有详细说明差异是什么,它们是否仅限于排序差异,或者是否可以找到如果不需要通用解决方案,则等效于特定 SQL 排序规则的 StringComparer。
(旁注 - 缓存层是通用的,所以我不能对 key 的性质以及合适的排序规则做出特定假设。我数据库中的所有表都共享相同的默认服务器排序规则。我只是需要匹配存在的排序规则)
最佳答案
我最近遇到了同样的问题:我需要一个 IEqualityComparer<string>
以类似 SQL 的方式运行。我试过了 CollationInfo
及其 EqualityComparer
.如果您的数据库始终是 _AS(区分重音),那么您的解决方案将起作用,但如果您更改了 AI 或 WI 的排序规则或任何“不敏感”的东西,否则哈希会中断。
为什么?如果你反编译 Microsoft.SqlServer.Management.SqlParser.dll 并查看内部你会发现 CollationInfo
内部使用 CultureAwareComparer.GetHashCode
(它是 mscorlib.dll 的内部类),最后它执行以下操作:
public override int GetHashCode(string obj)
{
if (obj == null)
throw new ArgumentNullException("obj");
CompareOptions options = CompareOptions.None;
if (this._ignoreCase)
options |= CompareOptions.IgnoreCase;
return this._compareInfo.GetHashCodeOfString(obj, options);
}
如您所见,它可以为“aa”和“AA”生成相同的哈希码,但不能为“äå”和“aa”生成相同的哈希码(如果忽略大多数文化中的变音符号 (AI),它们是相同的,所以他们应该有相同的哈希码)。我不知道为什么 .NET API 受此限制,但您应该了解问题出在哪里。要为带有变音符号的字符串获得相同的哈希码,您可以执行以下操作:create implementation的 IEqualityComparer<T>
实现GetHashCode
这将调用适当的 CompareInfo
的对象的 GetHashCodeOfString
通过反射,因为这个方法是内部的,不能直接使用。但是直接用正确的 CompareOptions
调用它将产生所需的结果:看这个例子:
static void Main(string[] args)
{
const string outputPath = "output.txt";
const string latin1GeneralCiAiKsWs = "Latin1_General_100_CI_AI_KS_WS";
using (FileStream fileStream = File.Open(outputPath, FileMode.Create, FileAccess.Write))
{
using (var streamWriter = new StreamWriter(fileStream, Encoding.UTF8))
{
string[] strings = { "aa", "AA", "äå", "ÄÅ" };
CompareInfo compareInfo = CultureInfo.GetCultureInfo(1033).CompareInfo;
MethodInfo GetHashCodeOfString = compareInfo.GetType()
.GetMethod("GetHashCodeOfString",
BindingFlags.Instance | BindingFlags.NonPublic,
null,
new[] { typeof(string), typeof(CompareOptions), typeof(bool), typeof(long) },
null);
Func<string, int> correctHackGetHashCode = s => (int)GetHashCodeOfString.Invoke(compareInfo,
new object[] { s, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace, false, 0L });
Func<string, int> incorrectCollationInfoGetHashCode =
s => CollationInfo.GetCollationInfo(latin1GeneralCiAiKsWs).EqualityComparer.GetHashCode(s);
PrintHashCodes(latin1GeneralCiAiKsWs, incorrectCollationInfoGetHashCode, streamWriter, strings);
PrintHashCodes("----", correctHackGetHashCode, streamWriter, strings);
}
}
Process.Start(outputPath);
}
private static void PrintHashCodes(string collation, Func<string, int> getHashCode, TextWriter writer, params string[] strings)
{
writer.WriteLine(Environment.NewLine + "Used collation: {0}", collation + Environment.NewLine);
foreach (string s in strings)
{
WriteStringHashcode(writer, s, getHashCode(s));
}
}
输出是:
Used collation: Latin1_General_100_CI_AI_KS_WS
aa, hashcode: 2053722942
AA, hashcode: 2053722942
äå, hashcode: -266555795
ÄÅ, hashcode: -266555795
Used collation: ----
aa, hashcode: 2053722942
AA, hashcode: 2053722942
äå, hashcode: 2053722942
ÄÅ, hashcode: 2053722942
我知道它看起来像 hack,但在检查反编译的 .NET 代码后,我不确定是否有任何其他选项以防需要通用功能。因此请确保您不会陷入使用这个不完全正确的 API 的陷阱。
更新:
我还创建了 the gist with potential implementation of "SQL-like comparer"使用 CollationInfo
.还有就是要引起足够的重视where to search for "string pitfalls"在你的代码库中,所以如果字符串比较、哈希码、相等性应该更改为“类似 SQL 排序规则”,这些地方 100% 会被破坏,所以你必须找出并检查所有可能被破坏的地方.
更新#2:
有更好更简洁的方法让 GetHashCode() 处理 CompareOptions。有类 SortKey与 CompareOptions 一起正常工作,可以使用
CompareInfo.GetSortKey(yourString, yourCompareOptions).GetHashCode()
这是 link .NET 源代码和实现。
更新 #3:
如果您使用的是 .NET Framework 4.7.1+,则应使用新的 GlobalizationExtensions
class正如 this recent answer 所提议的那样.
关于c# - 什么.NET StringComparer 相当于 SQL 的 Latin1_General_CI_AS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9384642/
SQL、PL-SQL 和 T-SQL 之间有什么区别? 谁能解释一下这三者之间的区别,并提供每一个的相关使用场景? 最佳答案 SQL 是一种对集合进行操作的查询语言。 它或多或少是标准化的,几乎所有关
这个问题已经有答案了: What is the difference between SQL, PL-SQL and T-SQL? (6 个回答) 已关闭 9 年前。 我对 SQL 的了解足以完成我的
我在数据库中有一个 USER 表。该表有一个 RegistrationDate 列,该列有一个默认约束为 GETDATE()。 使用 LINQ 时,我没有为 RegistrationDate 列提供任
我有一个可能属于以下类型的字符串 string expected result 15-th-rp 15 15/12-rp 12 15-12-th
很难说出这里问的是什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或言辞激烈,无法以目前的形式合理回答。如需帮助澄清此问题以便可以重新打开,visit the help center . 9年前关闭
我有一个存储过程(称为 sprocGetArticles),它从文章表中返回文章列表。这个存储过程没有任何参数。 用户可以对每篇文章发表评论,我将这些评论存储在由文章 ID 链接的评论表中。 有什么方
我目前正在做一个 *cough*Oracle*cough* 数据库主题。讲师介绍embedded SQL作为让其他语言(例如 C、C++)与(Oracle)数据库交互的方式。 我自己做了一些数据库工作
SQL Server 中 SQL 语句的最大长度是多少?这个长度是否取决于 SQL Server 的版本? 例如,在 DECLARE @SQLStatement NVARCHAR(MAX) = N'S
这个问题已经有答案了: Simple way to transpose columns and rows in SQL? (9 个回答) 已关闭 8 年前。 CallType
预先感谢您对此提供的任何帮助。 假设我有一个查询,可以比较跨年的数据,从某个任意年份开始,永无止境(进入 future ),每年同一时期直到最后一个完整的月份(其特点是一月数据永远不会显示至 2 月
我在数据库中有一个 USER 表。该表有一个 RegistrationDate 列,该列的默认约束为 GETDATE()。 使用 LINQ 时,我没有为 RegistrationDate 列提供任何数
下面是我试图用来检查存储过程是否不存在然后创建过程的 sql。它会抛出一个错误:Incorrect syntax near the keyword 'PROCEDURE' IF NOT EXISTS
我有一个同事声称动态 SQL 在许多情况下比静态 SQL 执行得更快,所以我经常看到 DSQL 到处都是。除了明显的缺点,比如在运行之前无法检测到错误并且更难阅读,这是否准确?当我问他为什么一直使用
来自 lobodava 的动态 SQL 查询是: declare @sql nvarchar(4000) = N';with cteColumnts (ORDINAL_POSITION, CO
使用 SQL Server 中的存储过程执行动态 SQL 命令的现实优点和缺点是什么 EXEC (@SQL) 对比 EXEC SP_EXECUTESQL @SQL ? 最佳答案 sp_executes
我有这个有效的 SQL 查询: select sum(dbos.Points) as Points, dboseasons.Year from dbo.StatLines dbos i
我正在调试一些构建成功运行的 SQL 命令的代码。 然而,在查询结束时,查询结果似乎被写入了一个文本文件。 完整的查询如下 echo SELECT DATE,DATETABLE,DATE,APPDAT
我有一些创建表的 .sql 文件(MS SQL 数据库): 表_1.sql: IF OBJECT_ID (N'my_schema.table1', N'U') IS NOT NULL DROP TAB
我写了下面的 SQL 存储过程,它一直给我错误@pid = SELECT MAX(... 整个过程是: Alter PROCEDURE insert_partyco @pname varchar(20
我在 SQL Server 2005 中有包含两列 Fruit 和 Color 的表,如下所示 Fruit Colour Apple Red Orange
我是一名优秀的程序员,十分优秀!