David Clarke David Clarke - 12 days ago 5
C# Question

How to inject CacheItemPolicy using Simple Injector

I am following some practices documented by steven and using Simple Injector. I have a query that retrieves data from a WCF service and I want to cache the result using an instance of

ObjectCache
.

I've defined a decorator
CachingQueryHandlerDecorator<TQuery, TResult>
:

public sealed class CachingQueryHandlerDecorator<TQuery, TResult>
: IQueryHandler<TQuery, TResult>
where TQuery : IQuery<TResult>
{
private readonly IQueryHandler<TQuery, TResult> _handler;
private readonly ObjectCache _cache;
private readonly CacheItemPolicy _policy;
private readonly ILog _log;

public CachingQueryHandlerDecorator(IQueryHandler<TQuery, TResult> handler,
ObjectCache cache,
CacheItemPolicy policy,
ILog log)
{
_handler = handler;
_cache = cache;
_policy = policy;
_log = log;
}

public TResult Handle(TQuery query)
{
var key = query.GetType().ToString();
var result = (TResult) _cache[key];
if (result == null)
{
_log.Debug(m => m("No cache entry for {0}", key));
result = (TResult)_handler.Handle(query);
if (!_cache.Contains(key))
_cache.Add(key, result, _policy);
}
return result;
}
}


Within
SimpleInjectorInitializer.cs
I define the cache and policy, and add the decorator for a specific query:

container.RegisterSingle<ILog>(LogManager.GetCurrentClassLogger());
container.RegisterSingle<ObjectCache>(() => new MemoryCache("MyCache"));
container.RegisterSingle<CacheItemPolicy>(() => new CacheItemPolicy { AbsoluteExpiration = DateTime.Now.AddMinutes(1) } );
.
.
.
container.RegisterDecorator(typeof(IQueryHandler<,>),
typeof(CachingQueryHandlerDecorator<,>),
ctx => ctx.ServiceType.GetGenericArguments()[0] == typeof(MyQuery));


The problem I'm facing is that I want to be able to specify different
CacheItemPolicy
's for different queries. I could create a new
ICachePolicy<TQuery>
interface and then define concrete classes for each different query type but I'm hoping there might be a way to avoid that and define the policy per query directly in the initialization file.

Answer

I could create a new ICachePolicy interface and then define concrete classes for each different query type

I think that's a pretty neat idea actually. You can register a default generic implementation that injected into every decorator that has no specific implementation registered:

container.RegisterOpenGeneric(typeof(ICachePolicy<>), typeof(DefaultCachePolicy<>),
    Lifestyle.Singleton);

And for queries that have an alternative cache policy, you can register a specific implementation:

container.RegisterSingle<ICachePolicy<MyQuery>>(new CachePolicy<MyQuery> 
{
    AbsoluteExpiration = DateTime.Now.AddHour(2)
});

Another option is to mark queries or their query handlers with an attribute that describes the caching policy (this is the route I usually take):

[CachePolicy(AbsoluteExpirationInSeconds = 1 * 60 * 60)]
public class MyQuery : IQuery<string[]> { }

Now you don't have to inject an ICachePolicy<T>, but can read this metadata directly using reflection:

public sealed class CachingQueryHandlerDecorator<TQuery, TResult>
    : IQueryHandler<TQuery, TResult>
    where TQuery : IQuery<TResult>
{
    private static readonly bool shouldCache;
    private static readonly CachingPolicySettings policy;

    private readonly IQueryHandler<TQuery, TResult> _handler;
    private readonly ObjectCache _cache;
    private readonly ILog _log;

    static CachingQueryHandlerDecorator()
    {
        var attribute = typeof(TQuery).GetCustomAttribute<CachePolicyAttribute>();

        if (attribute != null)
        {
            shouldCache = true;
            policy = attribute.Policy;
        }
    }

    public CachingQueryHandlerDecorator(
        IQueryHandler<TQuery, TResult> handler,
        ObjectCache cache,
        ILog log)
    {
        _handler = handler;
        _cache = cache;
        _log = log;
    }

    public TResult Handle(TQuery query)
    {
        if (!shouldCache)
        {
            return this._handler.handle(query);
        }

        // do your caching stuff here.
    }
Comments