How to inject CacheItemPolicy with a simple injector

I follow some practices documented by steven and using a simple injector . I have a query that retrieves data from a WCF service, and I want to cache the result using an ObjectCache instance.

I defined the CachingQueryHandlerDecorator<TQuery, TResult> decorator:

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

Inside SimpleInjectorInitializer.cs I define a cache and a policy and add a decorator for a specific request:

 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 that I am facing is that I want to be able to specify different CacheItemPolicy for different requests. I could create a new ICachePolicy<TQuery> interface and then define specific classes for each type of request, but I hope there can be a way to avoid this and define a policy for each request directly in the initialization file.

+6
source share
2 answers

I could create a new ICachePolicy interface and then define specific classes for each type of request

I think this is actually a pretty neat idea. You can register a standard default implementation, which is entered into each decorator that does not have a specific implementation registered:

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

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

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

Another option is to mark requests or request handlers with an attribute that describes the cache policy (usually this is a route):

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

Now you do not need to enter ICachePolicy<T> , but you 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. } 
+12
source

You can achieve the desired result with an open common implementation and override the default values ​​as needed. That is, you define an open shared implementation of CachePolicy<TQuery> ICachePolicy<TQuery> and use the RegisterInitializer method to override the default implementation parts.

Given these definitions:

 public interface ICachePolicy<TQuery> { DateTime AbsoluteExpiration { get; } } public class CachePolicy<TQuery> : ICachePolicy<TQuery> { public CachePolicy() { AbsoluteExpiration = Cache.NoAbsoluteExpiration; } public DateTime AbsoluteExpiration { get; set; } } public interface IQueryHandler<TQuery, TResult> { } public class QueryHandlerA : IQueryHandler<A, AResult> { } public class QueryHandlerB : IQueryHandler<B, BResult> { } public sealed class CachingQueryHandlerDecorator<TQuery, TResult> : IQueryHandler<TQuery, TResult> { private readonly IQueryHandler<TQuery, TResult> decorated; public readonly ICachePolicy<TQuery> Policy; public CachingQueryHandlerDecorator( IQueryHandler<TQuery, TResult> decorated, ICachePolicy<TQuery> cachePolicy) { this.decorated = decorated; this.Policy = cachePolicy; } } 

Configure the container using the RegisterOpenGeneric method and configure the default values ​​using RegisterInitializer :

 public Container ConfigureContainer() { Container container = new Container(); container.RegisterOpenGeneric( typeof(ICachePolicy<>), typeof(CachePolicy<>), Lifestyle.Singleton); container.RegisterInitializer<CachePolicy<A>>(a => a.AbsoluteExpiration = DateTime.Now.AddMinutes(1)); container.RegisterManyForOpenGeneric( typeof(IQueryHandler<,>), typeof(IQueryHandler<,>).Assembly); container.RegisterDecorator( typeof(IQueryHandler<,>), typeof(CachingQueryHandlerDecorator<,>)); container.Verify(); return container; } 

These tests demonstrate the result as expected:

 [Test] public void GetInstance_A_HasCustomAbsoluteExpiration() { Container container = ConfigureContainer(); var a = container.GetInstance<IQueryHandler<A, AResult>>(); Assert.AreNotEqual( (a as CachingQueryHandlerDecorator<A, AResult>).Policy.AbsoluteExpiration, Cache.NoAbsoluteExpiration); } [Test] public void GetInstance_B_HasDefaultAbsoluteExpiration() { Container container = ConfigureContainer(); var b = container.GetInstance<IQueryHandler<B, BResult>>(); Assert.AreEqual( (b as CachingQueryHandlerDecorator<B, BResult>).Policy.AbsoluteExpiration, Cache.NoAbsoluteExpiration); } 
+7
source

Source: https://habr.com/ru/post/957472/


All Articles