具有附加依赖性的简单注入器装饰

Simple Injector decoration with additional dependency

我有一个装饰器 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);
    }
}

如何使用简单注入器用 SomethingLoggerDecorator 装饰 ISomething 的实例,并使用静态工厂方法 LogManager.GetLogger(decoratee.GetType())ILogger 的实例注入到每个装饰器中] decoratee 是要被装饰的实际实例?此外,注入的 ILoggerSomethingLoggerDecorator 的生命周期应始终与装饰者的生命周期相匹配。

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

首先,你可以让你的装饰器的构造函数依赖于DecoratorContext class。 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 class 的缺点是你的装饰器需要依赖简单注入器,这基本上意味着你必须将装饰器移动到你的 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.

另一种选择是使您的装饰器通用,并向 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
    }
}