Skip to content

Instantly share code, notes, and snippets.

@lakario
Created September 4, 2018 15:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lakario/8e10da22ab4117eef688ae8b8b1db50e to your computer and use it in GitHub Desktop.
Save lakario/8e10da22ab4117eef688ae8b8b1db50e to your computer and use it in GitHub Desktop.
static void Main()
{
var builder = new ContainerBuilder();
builder.RegisterType<SomeDependency>().SingleInstance();
builder.RegisterTypeWithCaching<MyApplicationService>().SingleInstance();
var container = builder.Build();
var proxy = container.Resolve<MyApplicationService>();
TestCase1(proxy);
TestCase2(proxy);
TestCase3(proxy);
TestCase1(proxy);
TestCase2(proxy);
TestCase3(proxy);
Console.WriteLine("Sleeping for 1000 ms...\n");
Thread.Sleep(1000);
TestCase1(proxy);
TestCase2(proxy);
TestCase3(proxy);
TestCase1(proxy);
TestCase2(proxy);
TestCase3(proxy);
}
static void TestCase1(MyApplicationService service)
{
Console.Write($"[Cached(15)] {nameof(MyApplicationService)}.{nameof(service.MethodA)}(\"foo\", \"bar\", 50): ");
Console.WriteLine(service.MethodA("foo", "bar", 50));
}
static void TestCase2(MyApplicationService service)
{
Console.Write($"[Cached(15)] {nameof(MyApplicationService)}.{nameof(service.MethodA)}(\"bar\", \"foo\", 50): ");
Console.WriteLine(service.MethodA("bar", "foo", 50));
}
static void TestCase3(MyApplicationService service)
{
Console.Write($"{nameof(MyApplicationService)}.{nameof(service.MethodB)}(): ");
Console.Write(service.MethodB() + "\n\n");
}
public static class AutofacExtensions
{
private static readonly ProxyGenerator _proxyGenerator = new ProxyGenerator();
public static IRegistrationBuilder<T, SimpleActivatorData, SingleRegistrationStyle>
RegisterTypeWithCaching<T>(this ContainerBuilder builder)
{
return builder.Register(ctx =>
{
var args = typeof(T)
.GetConstructors()
.Select(b => b.GetParameters())
.OrderByDescending(b => b.Length)
.First()
.Select(t => ctx.Resolve(t.ParameterType)).ToArray();
return (T)_proxyGenerator.CreateClassProxy(typeof(T), args, new CachingInterceptor());
})
.As(typeof(T));
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
public class CachedAttribute : Attribute
{
public double? DurationMs { get; set; }
public CachedAttribute()
{
}
public CachedAttribute(double durationMs)
{
DurationMs = durationMs;
}
}
public class SomeDependency
{
public SomeDependency()
{
}
}
public class MyApplicationService
{
private static readonly Random _random = new Random();
public MyApplicationService(SomeDependency dependency)
{
}
[Cached(15)]
public virtual int MethodA(string input1, string input2, int input3)
{
return _random.Next(0, input3);
}
public virtual int MethodB()
{
return _random.Next(0, 1000);
}
}
public class CachingInterceptor : IInterceptor
{
private readonly ICache _cache = new InMemoryCache();
public void Intercept(IInvocation invocation)
{
var cachedAttribute = invocation.Method.GetCustomAttribute<CachedAttribute>();
if (cachedAttribute == null)
{
invocation.Proceed();
}
else
{
var arguments = String.Join(",", invocation.Arguments.Select(a => $"{a.GetType().Name}:{a.GetHashCode()}"));
var hashKey = $"{invocation.Method.DeclaringType.FullName}.{invocation.Method.Name}({arguments})";
if (!_cache.ContainsKey(hashKey))
{
invocation.Proceed();
var cacheDuration = cachedAttribute.DurationMs.HasValue
? TimeSpan.FromMilliseconds(cachedAttribute.DurationMs.Value)
: (TimeSpan?)null;
_cache.Add(hashKey, invocation.ReturnValue, cacheDuration);
}
else
{
invocation.ReturnValue = _cache[hashKey];
}
}
}
}
// plubming...
public interface ICache
{
void Add(string key, object value, TimeSpan? duration = null);
object Get(string key);
object this[string key] { get; }
bool ContainsKey(string key);
}
public class InMemoryCache : ICache
{
private readonly Dictionary<string, CacheEntry> _inMemoryCache = new Dictionary<string, CacheEntry>();
public object this[string key]
{
get { return Get(key); }
}
public void Add(string key, object value, TimeSpan? duration = null)
{
var cacheEntry = new CacheEntry(value, duration);
_inMemoryCache[key] = cacheEntry;
}
public object Get(string key)
{
if (!_inMemoryCache.ContainsKey(key) || _inMemoryCache[key].IsExpired)
{
return null;
}
return _inMemoryCache[key].Value;
}
public bool ContainsKey(string key)
{
return _inMemoryCache.ContainsKey(key) && !_inMemoryCache[key].IsExpired;
}
private class CacheEntry
{
public DateTime CreatedDateTime { get; } = DateTime.UtcNow;
public TimeSpan? Duration { get; }
public object Value { get; }
public bool IsExpired { get { return Duration.HasValue ? (DateTime.UtcNow - CreatedDateTime) > Duration.Value : false; } }
public CacheEntry(object value, TimeSpan? duration)
{
Value = value;
Duration = duration;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment