Search code examples
c#.netdependency-injectionsimple-injector

How to inject additional dependencies / configuration into a decorated class


I have an IRepository<> and some Setting entity. I know that for this entity I will need a cached repository so I want to create a ICachedRepository<Setting> decorator and register it with Simple Injector.

The problem is that I have additional configuration that I need to pass somehow to the cached repository, e.g. expiration time span, prefetch policy, etc.

CachedRepository(IRepository repository, CachedRepositoryConfiguration configuration)
{
   ...
}

How can I do that? (These configuration can of course be different for different repositories, e.g. 30 minutes for one, 1 hour for another)


Solution

  • Simple Injector does not easily allow you to configure the constructor values of your decorators (although this can be done by implementing a custom IDependencyInjectionBehavior - or IConstructorInjectionBehavior in v2).

    The easiest way around this is to fallback to property injection and register an initializer per decorator:

    public class CachingRepositoryDecorator<TEntity> : IRepository<TEntity>
    {
        private readonly IRepository<TEntity> decoratee;
        public CachingRepositoryDecorator(IRepository<TEntity> decoratee) {
            this.decoratee = decoratee;
            this.Configuration = CachedRepositoryConfiguration.NoCaching;
        }
    
        public CachedRepositoryConfiguration Configuration { get; set; }
    
        // IRepository<T> methods here.
    }
    
    container.RegisterDecorator(typeof(IRepository<>), 
        typeof(CachingRepositoryDecorator<>));
    
    container.RegisterInitializer<CachingRepositoryDecorator<Setting>>(decorator => {
       decorator.Configuration = 
           CachedRepositoryConfiguration.AbsoluteExpiration(TimeSpan.FromMinutes(30));
    });
    

    Another option is to use Context Based injection. With the extension method given in the documentation you can do the following:

    public class CachingRepositoryDecorator<TEntity> : IRepository<TEntity>
    {
        private readonly IRepository<TEntity> decoratee;
        private readonly CachedRepositoryConfiguration configuration;
        public CachingRepositoryDecorator(IRepository<TEntity> decoratee,
            CachedRepositoryConfiguration configuration) {
            this.decoratee = decoratee;
            this.configuration = configuration;
        }
    
        // IRepository<T> methods here.
    }
    
    container.RegisterDecorator(typeof(IRepository<>), 
        typeof(CachingRepositoryDecorator<>));
    
    container.RegisterWithContext<CachedRepositoryConfiguration>(context => {
        if (context.ImplementationType == typeof(CachingRepositoryDecorator<Setting>)) {
            return CachedRepositoryConfiguration.AbsExpiration(TimeSpan.FromMinutes(30));
        } else {
            CachedRepositoryConfiguration.NoCaching;
        }
    });
    

    A third option is to make the configuration object generic as follows:

    public class CachingRepositoryDecorator<TEntity> : IRepository<TEntity>
    {
        private readonly IRepository<TEntity> decoratee;
        private readonly CachedRepositoryConfiguration<TEntity> configuration;
        public CachingRepositoryDecorator(IRepository<TEntity> decoratee,
            CachedRepositoryConfiguration<TEntity> configuration) {
            this.decoratee = decoratee;
            this.configuration = configuration;
        }
    
        // IRepository<T> methods here.
    }
    
    container.RegisterDecorator(typeof(IRepository<>), 
        typeof(CachingRepositoryDecorator<>));
    
    container.RegisterSingleton<CachedRepositoryConfiguration<Setting>>(
        CachedRepositoryConfiguration<Setting>.Absolute(TimeSpan.FromMinutes(30)));
    
    container.RegisterSingleton<CachedRepositoryConfiguration<Customer>>(
        CachedRepositoryConfiguration<Customer>.Sliding(TimeSpan.FromMinutes(5)));
    
    // Register the 'no caching' configuration as fallback
    container.Register(
        typeof(CachedRepositoryConfiguration<>),
        typeof(NoCachingCachedRepositoryConfiguration<>).
        Lifestyle.Singleton);
    
    public sealed class NoCachingCachedRepositoryConfiguration<T>
        : CachedRepositoryConfiguration<T>
    {
        public NoCachingCachedRepositoryConfiguration() : base(cache: false) { }
    }
    

    This third option allows you to remove the conditional registration altogether.