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.
}