简单的注射器装饰,具有额外的依赖性

时间:2015-06-19 21:31:20

标签: c# logging dependency-injection decorator simple-injector

我有一个装饰器SomethingLoggerDecorator,它应该用日志来装饰ISomething个实例:

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

如何使用Simple Injector使用ISomething装饰SomethingLoggerDecorator的实例,并使用静态工厂方法ILoggerLogManager.GetLogger(decoratee.GetType())的实例注入每个装饰器中decoratee是要装饰的实际实例?注入的ILogger以及SomethingLoggerDecorator的生命周期也应始终与decoratee的生命周期相匹配。

1 个答案:

答案 0 :(得分:2)

在Simple Injector中有多种方法可以做到这一点。

首先,您可以让装饰器的构造函数依赖于DecoratorContext类。 DecoratorContext包含有关装饰器的上下文信息。它包含诸如包装装饰器的类型和装饰的实际实现的类型(真实实例)之类的信息。所以你的装饰者可能看起来如下:

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

使用这个DecoratorContext类的缺点是你的装饰者需要依赖于Simple Injector,这基本上意味着你必须在Composition Root内移动装饰器以防止你的应用程序依赖于DI库。您可以找到有关使用此DecoratorContext课程here的更多信息。

另一种选择是让你的装饰器通用,并为可以制作正确装饰器的RegisterDecorator重载之一提供装饰器类型工厂。例如:

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

或者可选:

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

通过这两种实现,您可以按如下方式注册装饰器:

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

使用第二个实现,您将把问题稍微移到泛型ILogger<T>抽象,但实现可能如下所示:

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