Ilya Chernomordik
Ilya Chernomordik

Reputation: 30295

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)

Upvotes: 1

Views: 141

Answers (1)

Steven
Steven

Reputation: 172816

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.

Upvotes: 1

Related Questions