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:
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

Popular posts from this blog

EMGU with Xamarin Forms guide [part 1]

[Xamarin Forms] Custom bottom bordered entry (iOS & Android)

Set your browser on Fire with P5.js