Simple Injector decoration with additional dependency

466 views Asked by At

I have a decorator SomethingLoggerDecorator that is supposed to decorate ISomething instances with logging:

public class SomethingLoggerDecorator : ISomething
{
    private readonly ISomething decoratee;
    private readonly ILogger logger;

    public SomethingLoggerDecorator(ISomething decoratee, ILogger logger)
    {
        this.decoratee = decoratee;
        this.logger = logger;
    }

    public void DoSomething()
    {
        this.logger.Info("Doing Something");
        this.decoratee.DoSomething();
    }

    public void DoSomethingElse(string withThis)
    {
        this.logger.Info("Doing Something Else with " + withThis);
        this.decoratee.DoSomethingElse(withThis);
    }
}

How can I use Simple Injector to decorate instances of ISomething with SomethingLoggerDecorator and inject an instance of ILoggerinto each of those decorators using the static factory method LogManager.GetLogger(decoratee.GetType()) where decoratee is the actual instance that is to be decorated? Also the lifetime of the injected ILogger as well as the SomethingLoggerDecorator should always match the lifetime of the decoratee.

1

There are 1 answers

1
Steven On BEST ANSWER

There are multiple ways to do this in Simple Injector.

First of all, you can let your decorator's constructor depend on the DecoratorContext class. The DecoratorContext contains contextual information about the decorator. It contains information such as the types of the wrapped decorators and the type of the actual implementation that is decorated (the real instance). So your decorator might look as follows:

public class SomethingLoggerDecorator : ISomething
{
    private readonly ISomething decoratee;
    private readonly DecoratorContext context;

    public SomethingLoggerDecorator(ISomething decoratee, DecoratorContext context)
    {
        this.decoratee = decoratee;
        this.context = context;
    }

    public void DoSomething()
    {
        var logger = LogManager.GetLogger(this.context.ImplementationType);
        logger.Info("Doing Something");
        this.decoratee.DoSomething();
    }
}

Downside of the use of this DecoratorContext class is that your decorator needs to take a dependency on Simple Injector, which basically means you will have to move the decorator inside your Composition Root to prevent your application from taking a dependency on the DI library. You can find more information about the use of this DecoratorContext class here.

Another option is to make your decorator generic and supply a decorator type factory to one of the RegisterDecorator overload that can make the correct decorator. For instance:

public class SomethingLoggerDecorator<TImplementation> : ISomething
{
    private readonly ISomething decoratee;

    public SomethingLoggerDecorator(ISomething decoratee)
    {
        this.decoratee = decoratee;
    }

    public void DoSomething()
    {
        var logger = LogManager.GetLogger(typeof(TImplementation));
        logger.Info("Doing Something");
        this.decoratee.DoSomething();
    }
}

Or optionally:

public class SomethingLoggerDecorator<TImplementation> : ISomething
{
    private readonly ISomething decoratee;
    private readonly ILogger<TImplementation> logger;

    public SomethingLoggerDecorator(ISomething decoratee, ILogger<TImplementation> logger)
    {
        this.decoratee = decoratee;
        this.logger = logger;
    }

    public void DoSomething()
    {
        this.logger.Info("Doing Something");
        this.decoratee.DoSomething();
    }
}

With both implementations, you can register the decorator as follows:

container.RegisterDecorator(typeof(ISomething),
    c => typeof(SomethingLoggerDecorator<>).MakeGenericType(c.ImplementationType),
    Lifestyle.Transient,
    predicate: c => true);

With the second implementation, you will move the problem a bit to the generic ILogger<T> abstraction, but an implementation could look as follows:

public class Log4netAdapter<T> : ILogger<T>
{
    public void Log(LogEntry entry) {
        var logger = LogManager.GetLogger(typeof(T));
        // TODO: log
    }
}