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