C# 基于方法名和参数值的缓存键构造
我决定在我们的一个应用程序中实现一个缓存facade,其目的是最终减少网络开销并限制数据库点击量。我们正在使用C# 基于方法名和参数值的缓存键构造,c#,caching,hash,dictionary,C#,Caching,Hash,Dictionary,我决定在我们的一个应用程序中实现一个缓存facade,其目的是最终减少网络开销并限制数据库点击量。我们正在使用Castle.Windsor作为我们的IoC容器,我们决定使用拦截器在我们的服务层上添加缓存功能,使用System.Runtime.caching命名空间 此时此刻,我无法准确地找出构造缓存键的最佳方法。目标是区分不同的方法,还包括传递的参数值——这意味着这两个方法调用应该缓存在两个不同的键下: IEnumerable<MyObject> GetMyObjectByParam
Castle.Windsor
作为我们的IoC容器
,我们决定使用拦截器
在我们的服务层上添加缓存功能,使用System.Runtime.caching
命名空间
此时此刻,我无法准确地找出构造缓存键的最佳方法。目标是区分不同的方法,还包括传递的参数值——这意味着这两个方法调用应该缓存在两个不同的键下:
IEnumerable<MyObject> GetMyObjectByParam(56); // key1
IEnumerable<MyObject> GetMyObjectByParam(23); // key2
我在考虑第一个选项,因为第二个选项需要更多的处理时间——另一方面,第二个选项强制所有生成密钥的“长度”完全相同
假设第一个选项将为使用复杂参数类型的方法生成唯一键是否安全?或者可能有一种完全不同的方法
非常感谢您的帮助和意见 您似乎跳过的一个选项是对字符串使用.NET内置函数。我很确定这是C#dictionary中的幕后故事,字符串是
(我提到这一点是因为你用dictionary标记了这个问题)。我不确定.NET dictionary类与您提到的Castle.Windsor
或system.runtime.caching
接口的关系
您不想使用GetHashCode作为散列键的原因是,MicroSoft明确声明该功能不允许在没有警告的情况下在不同版本之间进行更改(例如提供更独特或执行速度更快的函数)。如果这个缓存严格地存在于内存中,那么这就不成问题,因为升级.NET framework将需要重新启动应用程序,清除缓存
为了澄清这一点,仅使用连接字符串(选项1)应该是足够唯一的。看起来您已经添加了所有可能的内容来唯一地限定您的方法
如果您最终将MD5或Sha256的字符串馈送到字典键中,那么程序可能会在幕后重新刷新该字符串。我已经有一段时间没有读到Dictionary类的内部工作原理了。如果将其保留为字典
(而不是自己使用int返回值作为键对字符串调用GetHashCode()),那么字典应该处理哈希代码本身的冲突
还要注意的是(至少根据我机器上运行的基准测试程序),MD5比SHA1快10%左右,是SHA256的两倍。GetHashCode()的速度大约是MD5的20倍(它不是加密安全的)。对长度在32到1024个字符之间的100000个随机生成的字符串进行哈希计算的总时间测试。但不管确切的数字是多少,使用加密安全的哈希函数作为密钥只会降低程序的速度
如果您愿意,我可以发布源代码进行比较。基于我找到的一些非常有用的链接,我决定或多或少地实现它,如下所示:
public sealed class CacheKey : IEquatable<CacheKey>
{
private readonly Type reflectedType;
private readonly Type returnType;
private readonly string name;
private readonly Type[] parameterTypes;
private readonly object[] arguments;
public User(Type reflectedType, Type returnType, string name,
Type[] parameterTypes, object[] arguments)
{
// check for null, incorrect values etc.
this.reflectedType = reflectedType;
this.returnType = returnType;
this.name = name;
this.parameterTypes = parameterTypes;
this.arguments = arguments;
}
public override bool Equals(object obj)
{
return Equals(obj as CacheKey);
}
public bool Equals(CacheKey other)
{
if (other == null)
{
return false;
}
for (int i = 0; i < parameterTypes.Count; i++)
{
if (!parameterTypes[i].Equals(other.parameterTypes[i]))
{
return false;
}
}
for (int i = 0; i < arguments.Count; i++)
{
if (!arguments[i].Equals(other.arguments[i]))
{
return false;
}
}
return reflectedType.Equals(other.reflectedType) &&
returnType.Equals(other.returnType) &&
name.Equals(other.name);
}
private override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 31 + reflectedType.GetHashCode();
hash = hash * 31 + returnType.GetHashCode();
hash = hash * 31 + name.GetHashCode();
for (int i = 0; i < parameterTypes.Count; i++)
{
hash = hash * 31 + parameterTypes[i].GetHashCode();
}
for (int i = 0; i < arguments.Count; i++)
{
hash = hash * 31 + arguments[i].GetHashCode();
}
return hash;
}
}
}
公共密封类CacheKey:IEquatable
{
私有只读类型reflectedType;
私有只读类型returnType;
私有只读字符串名称;
私有只读类型[]参数类型;
私有只读对象[]参数;
公共用户(类型reflectedType、类型returnType、字符串名称、,
类型[]参数类型,对象[]参数)
{
//检查空值、不正确值等。
this.reflectedType=reflectedType;
this.returnType=returnType;
this.name=名称;
this.parameterTypes=parameterTypes;
this.arguments=参数;
}
公共覆盖布尔等于(对象对象对象)
{
返回等于(obj作为CacheKey);
}
公共布尔等于(缓存键其他)
{
如果(其他==null)
{
返回false;
}
对于(int i=0;i
基本上,这只是一个一般性的想法-通过一个字段集合
,可以轻松地将上述代码重写为更通用的版本-必须对集合的每个元素应用相同的规则。我可以分享完整的代码。嗨,帕特里克,我没有选择选项1有两个原因-首先,有时我使用的是相当大的层次对象,在键构造期间很难足够快地序列化,在其他情况下,我意识到String.GetHashCode
方法实际上返回相同的值并非不可能。那个
string key = new SHA256Managed().ComputeHash(name + args).ToString();
public sealed class CacheKey : IEquatable<CacheKey>
{
private readonly Type reflectedType;
private readonly Type returnType;
private readonly string name;
private readonly Type[] parameterTypes;
private readonly object[] arguments;
public User(Type reflectedType, Type returnType, string name,
Type[] parameterTypes, object[] arguments)
{
// check for null, incorrect values etc.
this.reflectedType = reflectedType;
this.returnType = returnType;
this.name = name;
this.parameterTypes = parameterTypes;
this.arguments = arguments;
}
public override bool Equals(object obj)
{
return Equals(obj as CacheKey);
}
public bool Equals(CacheKey other)
{
if (other == null)
{
return false;
}
for (int i = 0; i < parameterTypes.Count; i++)
{
if (!parameterTypes[i].Equals(other.parameterTypes[i]))
{
return false;
}
}
for (int i = 0; i < arguments.Count; i++)
{
if (!arguments[i].Equals(other.arguments[i]))
{
return false;
}
}
return reflectedType.Equals(other.reflectedType) &&
returnType.Equals(other.returnType) &&
name.Equals(other.name);
}
private override int GetHashCode()
{
unchecked
{
int hash = 17;
hash = hash * 31 + reflectedType.GetHashCode();
hash = hash * 31 + returnType.GetHashCode();
hash = hash * 31 + name.GetHashCode();
for (int i = 0; i < parameterTypes.Count; i++)
{
hash = hash * 31 + parameterTypes[i].GetHashCode();
}
for (int i = 0; i < arguments.Count; i++)
{
hash = hash * 31 + arguments[i].GetHashCode();
}
return hash;
}
}
}