CachingCallHandler and Unity 2.0
Unity 2.0 no longer includes CachingCallHandler, as you can see in here. Since this is something I use very often, I decided to bring it back, with some changes:
- It no longer uses ASP.NET cache, but the new MemoryCache
- The current process is part of the cache key
- The full method signature is included as part of the key, not just it's name
Otherwise, it is based on the source code from Enterprise Library 4.1.
[Serializable]
[ConfigurationElementType(typeof(CustomCallHandlerData))]
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class CachingCallHandlerAttribute : HandlerAttribute, ICallHandler
{
#region Private fields
private readonly Guid KeyGuid = new Guid("ECFD1B0F-0CBA-4AA1-89A0-179B636381CA");
private TimeSpan expirationTime = new TimeSpan(0, 5, 0);
#endregion
#region Public Constructors
public CachingCallHandlerAttribute()
{
}
public CachingCallHandlerAttribute(Int32 hours, Int32 minutes, Int32 seconds)
{
this.expirationTime = new TimeSpan(hours, minutes, seconds);
}
#endregion
#region Public override methods
public override ICallHandler CreateHandler(IUnityContainer ignored)
{
return (this);
}
#endregion
#region ICallHandler Members
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
if (this.targetMethodReturnsVoid(input) == true)
{
return (getNext()(input, getNext));
}
Object [] inputs = new Object [ input.Inputs.Count ];
for (Int32 i = 0; i < inputs.Length; ++i)
{
inputs [ i ] = input.Inputs [ i ];
}
String cacheKey = this.createCacheKey(input.MethodBase, inputs);
ObjectCache cache = MemoryCache.Default;
Object [] cachedResult = (Object []) cache.Get(cacheKey);
if (cachedResult == null)
{
IMethodReturn realReturn = getNext()(input, getNext);
if (realReturn.Exception == null)
{
this.addToCache(cacheKey, realReturn.ReturnValue);
}
return (realReturn);
}
IMethodReturn cachedReturn = input.CreateMethodReturn(cachedResult [ 0 ], input.Arguments);
return (cachedReturn);
}
#endregion
#region Private methods
private Boolean targetMethodReturnsVoid(IMethodInvocation input)
{
MethodInfo targetMethod = input.MethodBase as MethodInfo;
return ((targetMethod != null) && (targetMethod.ReturnType == typeof(void)));
}
private void addToCache(String key, Object value)
{
ObjectCache cache = MemoryCache.Default;
Object [] cacheValue = new Object [] { value };
cache.Add(key, cacheValue, DateTime.Now + this.expirationTime);
}
private String createCacheKey(MethodBase method, params Object [] inputs)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0}:", Process.GetCurrentProcess().Id);
sb.AppendFormat("{0}:", KeyGuid);
if (method.DeclaringType != null)
{
sb.Append(method.DeclaringType.FullName);
}
sb.Append(':');
sb.Append(method);
if (inputs != null)
{
foreach (Object input in inputs)
{
sb.Append(':');
if (input != null)
{
sb.Append(input.GetHashCode().ToString());
}
}
}
return (sb.ToString());
}
#endregion
}