Skip to content

Instantly share code, notes, and snippets.

@miklund
Last active February 20, 2019 19:43
Show Gist options
  • Save miklund/5802719 to your computer and use it in GitHub Desktop.
Save miklund/5802719 to your computer and use it in GitHub Desktop.
A simple cache interceptor using Castle.DynamicProxy2.
namespace LiteMedia.Cache
{
public static class CacheArg<T>
{
public static readonly T Any = default(T);
}
}
namespace LiteMedia.Cache
{
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Caching;
using Castle.DynamicProxy;
[Serializable]
[Export]
public class CacheInterceptor<T> : IInterceptor, IDisposable
{
[NonSerialized]
private readonly MemoryCache cache;
private readonly List<MethodInfo> cacheDirectory;
private bool disposed;
public CacheInterceptor()
{
cache = new MemoryCache(GetType().Name);
cacheDirectory = new List<MethodInfo>();
}
~CacheInterceptor()
{
Dispose(false);
}
public void Cache(Expression<Func<T, object>> target)
{
if (target == null)
{
throw new ArgumentNullException("target", "You're trying to watch null for cached values. Are you stupid?");
}
MethodCallExpression expression = null;
if (target.Body is UnaryExpression)
{
expression = ((UnaryExpression)target.Body).Operand as MethodCallExpression;
}
expression = expression ?? target.Body as MethodCallExpression;
if (expression == null)
{
throw new InvalidOperationException("Unable to get method info from expression.");
}
cacheDirectory.Add(expression.Method);
}
public void Intercept(IInvocation invocation)
{
if (invocation == null)
{
throw new ArgumentNullException("invocation", "It is highly unlikely that you're trying to intercept on a null instance since that would give you compile time errors. But what the heck! You might be doing something really inventive.");
}
if (cacheDirectory.Contains(invocation.Method))
{
var key = invocation.Arguments.Aggregate(string.Empty, (acc, item) => acc + item);
if (cache.Contains(key))
{
invocation.ReturnValue = cache[key];
}
else
{
invocation.Proceed();
cache.Add(key, invocation.ReturnValue, DateTime.UtcNow.AddDays(1));
}
}
else
{
invocation.Proceed();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// managed resources
}
// unmanaged resources
cache.Dispose();
}
disposed = true;
}
}
}
namespace LiteMedia.Cache
{
using Castle.DynamicProxy;
public static class CastleExtensions
{
public static T InterceptWith<T>(this T target, IInterceptor interceptor)
where T : class
{
// no interceptor - return target as is
if (interceptor == null)
{
return target;
}
var factory = new Castle.DynamicProxy.ProxyGenerator();
return factory.CreateInterfaceProxyWithTarget(target, interceptor);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment