Stu*_*art 13 .net c# sql-server collation stringcomparer
我正在我的数据库和我的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();
}
Run Code Online (Sandbox Code Playgroud)
StringComparer与数据库的排序规则相匹配的重要原因是误报和漏报都会对代码产生不良影响.
如果StringComparer说当数据库认为它们是不同的时,两个密钥A和B是相等的,那么数据库中可能有两行带有这两个密钥,但是如果要求A和缓存,则缓存将阻止第二个密钥返回B连续 - 因为B的get将错误地命中缓存并返回为A检索的对象.
如果StringComparer在数据库认为它们相同时说A和B不同,那么问题会更加微妙,但问题就不那么严重了.对两个键的GetObject调用都没问题,并返回对应于同一数据库行的对象.但是然后用密钥A调用SaveObject会使缓存不正确; 对于具有旧数据的密钥B,仍然存在缓存条目.随后的GetObject(B)将提供过时的信息.
因此,为了使我的代码正常工作,我需要StringComparer来匹配数据库行为,以进行相等性测试和哈希码生成.到目前为止,我的谷歌搜索已经产生了很多关于SQL排序规则和.NET比较不完全等同的信息,但没有关于差异的详细信息,是否仅限于排序的差异,或者是否有可能找到StringComparer,如果不需要通用解决方案,则等效于特定的 SQL排序规则.
(旁注 - 缓存层是通用的,所以我不能对密钥的性质和适当的归类做出特别的假设.我的数据库中的所有表共享相同的默认服务器排序规则.我只需要匹配存在的整理)
yar*_*kan 10
我最近遇到了同样的问题:我需要一个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);
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的,它可以为"aa"和"AA"生成相同的哈希码,但不能用于"äå"和"aa"(如果你在大多数文化中忽略变音符号(AI),它们是相同的,所以它们应该具有相同的哈希码).我不知道为什么.NET API受此限制,但您应该了解问题的来源.要为具有变音符号的字符串获取相同的哈希码,您可以执行以下操作:创建实现的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));
}
}
Run Code Online (Sandbox Code Playgroud)
输出是:
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
Run Code Online (Sandbox Code Playgroud)
我知道它看起来像黑客,但在检查反编译的.NET代码后,我不确定是否有任何其他选项,以防需要通用功能.所以请确保使用这个不完全正确的API不会陷入陷阱.
更新:
我还创建了主旨与潜在实行"类似SQL的比较器"的使用CollationInfo.另外应该注意在你的代码库中搜索"字符串陷阱",所以如果字符串比较,hashcode,相等应该改为"SQL collation-like"那些地方是100%会被打破,所以你我们必须找出并检查所有可能被打破的地方.
更新#2:
有更好更清洁的方法使GetHashCode()处理CompareOptions.SortKey类可以与CompareOptions一起正常工作,可以使用它来检索
CompareInfo.GetSortKey(yourString,yourCompareOptions).GetHashCode()
这是.NET源代码和实现的链接.
看看CollationInfo课程.它位于一个被称为的集合中, Microsoft.SqlServer.Management.SqlParser.dll虽然我不完全确定从哪里得到它.有一个静态的Collations(名称)列表和一个静态方法GetCollationInfo(按名称).
每个CollationInfo都有一个Comparer.它与a不完全相同,StringComparer但具有类似的功能.
编辑: Microsoft.SqlServer.Management.SqlParser.dll是共享管理对象(SMO)包的一部分.可以在此处下载SQL Server 2008 R2的此功能:
http://www.microsoft.com/download/en/details.aspx?id=16978#SMO
编辑: CollationInfo确实有一个命名属性EqualityComparer这是一个IEqualityComparer<string>.
| 归档时间: |
|
| 查看次数: |
3095 次 |
| 最近记录: |