AOP through Unity interceptors - Caching
Let's continue our serie on AOP with another popular usage of interception: caching.
Let's use the previously created project and add this class:
As you can see, we use the IgnoreCacheAttribute in order to avoid apply cache on choosen methods.
Here is its implementation:
We have a global cache for every Declaring type of our application. When a method is called with the same parameters, we get info from cache instead of calling the real underlying object.
Here is a graphical explanation of how is structured our cache:
Don't forget to register:
Let's use the previously created project and add this class:
public class CachingInterceptionBehavior : IInterceptionBehavior { private static IDictionary<string, MemoryCache> Cache = new Dictionary<string, MemoryCache>(); private static IList<string> SyncLock = new List<string>(); private static object SyncRoot = new object(); public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { if (input.MethodBase.GetCustomAttribute<IgnoreCacheAttribute>() != null) { return getNext()(input, getNext); } var arguments = JsonConvert.SerializeObject(input.Arguments); string key = string.Concat(input.MethodBase.DeclaringType.FullName, "|", input.MethodBase.ToString(), "|", arguments); if (IsInCache(input.MethodBase.DeclaringType, key)) { return input.CreateMethodReturn(GetFromCache(input.MethodBase.DeclaringType, key)); } lock (SyncRoot) { if (!SyncLock.Contains(key)) { SyncLock.Add(key); } } lock (SyncLock[SyncLock.IndexOf(key)]) { if (IsInCache(input.MethodBase.DeclaringType, key)) { return input.CreateMethodReturn(GetFromCache(input.MethodBase.DeclaringType, key)); } IMethodReturn result = getNext()(input, getNext); if (result.ReturnValue != null) { AddToCache(input.MethodBase.DeclaringType, key, result.ReturnValue); } return result; } } private void AddToCache(Type declaringType, string key, object toBeAddedToCache) { if (!Cache.ContainsKey(declaringType.FullName)) { Cache.Add(declaringType.FullName, new MemoryCache(declaringType.FullName)); } CacheItem item = new CacheItem(key, toBeAddedToCache); Cache[declaringType.FullName].Add(item, new CacheItemPolicy { AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddDays(1)) }); } private object GetFromCache(Type declaringType, string key) { return Cache[declaringType.FullName].Get(key); } private bool IsInCache(Type declaringType, string key) { return Cache.ContainsKey(declaringType.FullName) && Cache[declaringType.FullName].Contains(key); } public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } public bool WillExecute { get { return true; } } }
As you can see, we use the IgnoreCacheAttribute in order to avoid apply cache on choosen methods.
Here is its implementation:
public class IgnoreCacheAttribute : Attribute { }
We have a global cache for every Declaring type of our application. When a method is called with the same parameters, we get info from cache instead of calling the real underlying object.
Here is a graphical explanation of how is structured our cache:
Don't forget to register:
// Register container.RegisterType<IStore, StoreImpl>( new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<CachingInterceptionBehavior>()); container.RegisterType<IFoo, FooImpl>( new Interceptor<InterfaceInterceptor>(), new InterceptionBehavior<CachingInterceptionBehavior>());
Comments
Post a Comment