我在我的数据库和我的 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/
我在开发的Rails3网站的一些搜索功能上遇到了一个小问题。我有一个简单的Post模型,如下所示:classPost我正在使用acts_as_taggable_on来更轻松地向我的帖子添加标签。当我有一个标记为“rails”的帖子并执行以下操作时,一切正常:@posts=Post.tagged_with("rails")问题是,我还想搜索帖子的标题。当我有一篇标题为“Helloworld”并标记为“rails”的帖子时,我希望能够通过搜索“hello”或“rails”来找到这篇帖子。因此,我希望标题列的LIKE语句与acts_as_taggable_on提供的tagged_with方法
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
目录第1题连续问题分析:解法:第2题分组问题分析:解法:第3题间隔连续问题分析:解法:第4题打折日期交叉问题分析:解法:第5题同时在线问题分析:解法:第1题连续问题如下数据为蚂蚁森林中用户领取的减少碳排放量iddtlowcarbon10012021-12-1212310022021-12-124510012021-12-134310012021-12-134510012021-12-132310022021-12-144510012021-12-1423010022021-12-154510012021-12-1523.......找出连续3天及以上减少碳排放量在100以上的用户分析:遇到这类
我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时
Java的Collections.unmodifiableList和Collections.unmodifiableMap在Ruby标准API中是否有等价物? 最佳答案 使用freeze应用程序接口(interface):Preventsfurthermodificationstoobj.ARuntimeErrorwillberaisedifmodificationisattempted.Thereisnowaytounfreezeafrozenobject.SeealsoObject#frozen?.Thismethodretur
我有这个代码File.open(file_name,'r'){|file|file.read}但是Rubocop发出警告:Offenses:Style/SymbolProc:Pass&:readasargumenttoopeninsteadofablock.你是怎么做到的? 最佳答案 我刚刚创建了一个名为“t.txt”的文件,其中包含“Hello,World\n”。我们可以按如下方式阅读。File.open('t.txt','r',&:read)#=>"Hello,World\n"顺便说一下,由于第二个参数的默认值是'r',所以这样