Development With A Dot

Blog on development in general, and specifically on .NET

Sponsors

News

My Friends

My Links

Permanent Posts

Portuguese Communities

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
	}


Bookmark and Share

Comments

abhinav said:

The cachekey generation might not be optimum as for reference types the GetHashCode would return different values even if they are "Equal".

# September 29, 2012 6:29 PM