Generating unique keys based on a method or class
Quite often I during certain phases of my application development I find the need to generate unique values which are consistent when generated under the same circumstances. One situation I find myself needed this functionality is when implementing caching especially where data and databases are concerned.
It is always good to implement some form of caching, see why. I find its good to implement some form of caching either in my service or repository layers -- where best to implement caching is outside the scope of this article. One recurring theme I encounter is I need to cache the return value(s) of the method using the parameter(s) as the decider of whether the item should be cached or is already cached (key). With this in mind I thought it would be a great idea to generate a method unique cache key automatically this avoiding the need for hard-coded cache keys.
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection;
public static class UniqueKeys
{
private static readonly Dictionary<Tuple<Type, MethodBase, String>, string> Keys =
new Dictionary<Tuple<Type, MethodBase, String>, string>();
private readonly static object SyncObject = new object();
private const string SaltSeparator = "_";
///
/// Gets a method level unique key for the calling
/// method with an optional salt randomizer
///
public static string GetMethodKey(object salt = null)
{
var stackTrace = new StackTrace();
var callingMethod = stackTrace.GetFrame(1).GetMethod();
var @class = callingMethod.DeclaringType;
return GetMethodKey(@class, callingMethod, salt);
}
///
/// Gets a method level unique key for the specified
/// method and the class with an optional salt randomizer
///
public static string GetMethodKey(Type @class, MethodBase method, object salt)
{
lock (SyncObject)
{
var key = new Tuple<Type, MethodBase, String>(@class, method, salt != null ? salt.ToString() : String.Empty);
if (Keys.ContainsKey(key))
return Keys[key];
var parameters =
String.Join
(
SaltSeparator,
(method is MethodInfo) ? (method as MethodInfo).ReturnType.ToString() : String.Empty,
method.GetParameters()
.Select(x => String.Concat(x.Name, x.ParameterType.AssemblyQualifiedName))
.ToArray(),
method.IsGenericMethod ? method.GetGenericArguments() : new object[] {String.Empty}
);
var value = String.Format("{0}-{1}-{2}-{3}-{4}", @class.AssemblyQualifiedName, method.Name, parameters,
SaltSeparator, salt);
Keys.Add(key, value);
return value;
}
}
///
/// Gets a class level unique key with an optional randomizer
///
public static string GetClassKey<T>(object salt)
where T : class
{
lock (SyncObject)
{
var key = new Tuple<Type, MethodBase, String>(typeof (T), null, salt.ToString());
if (Keys.ContainsKey(key))
return Keys[key];
var genericParameters =
String.Join
(
SaltSeparator,
typeof (T).IsGenericType
? typeof (T).GetGenericParameterConstraints()
.Select(x => x.AssemblyQualifiedName)
.ToArray()
: new[] {String.Empty}
);
var value = String.Format("{0}-{1}{2}{3}", typeof (T).AssemblyQualifiedName, genericParameters, SaltSeparator, salt);
Keys.Add(key, value);
return value;
}
}
}
Fabulous review. Cool creama.Note to God: If you ever return my soul to this world, please give me more gray matter to keep up with and understand all this complex nerdy stuff so I can feel less nervous about how to function in a world with so many brainy people. Thanks!