多线程环境中的Ninject拦截

时间:2014-10-18 08:29:30

标签: dependency-injection ninject ninject-interception

我尝试使用Ninject.Extensions.Interception.DynamixProxy创建拦截器来记录方法完成时间。

在单线程环境中,类似这样的工作:

public class TimingInterceptor : SimpleInterceptor
{
    readonly Stopwatch _stopwatch = new Stopwatch();
    private bool _isStarted;


    protected override void BeforeInvoke(IInvocation invocation)
    {
        _stopwatch.Restart();
        if (_isStarted) throw new Exception("resetting stopwatch for another invocation => false results");
        _isStarted = true;
        invocation.Proceed();
    }

    protected override void AfterInvoke(IInvocation invocation)
    {
        Debug.WriteLine(_stopwatch.Elapsed);
        _isStarted = false;
    }
}

在多线程场景中,这不起作用,因为StopWatch在调用之间共享。如何将一个StopWatch实例从BeforeInvoke传递给AfterInvoke,这样它就不会在调用之间共享?

1 个答案:

答案 0 :(得分:3)

这在多线程应用程序中应该可以正常工作,因为每个线程都应该获得自己的对象图。因此,当您开始处理某个任务时,首先要解析一个新图形,并且图形不应该从一个线程传递给线程。这样可以将对线程安全(以及不是)集中的知识集中到应用程序中的一个地方,以便将所有内容连接起来:composition root

当你像这样工作时,这意味着当你使用这个拦截器来监视单例类(并在线程中使用)时,每个线程仍将获得自己的拦截器(当它被注册为瞬态时),因为每次你解决你得到一个新的拦截器(即使你重复使用相同的'拦截'实例)。

但这确实意味着你必须非常小心地将这个截取的组件注入其中,因为如果你将这个截取的对象注入另一个单例,你将再次遇到麻烦。这种特殊的麻烦'被称为captive dependency a.k.a生活方式不匹配。很容易意外错误配置你的容器让你自己陷入麻烦,不幸的是Ninject没有可能警告你这个。

请注意,如果您开始使用装饰器而不是拦截器,您的问题将会消失,因为使用装饰器可以将所有内容保存在单个方法中。这意味着即使装饰器也可以是单例,而不会引起任何线程问题。例如:

// Timing cross-cutting concern for command handlers
public class TimingCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
{
    private readonly ICommandHandler<TCommand> decoratee;

    public TimingCommandHandlerDecorator(ICommandHandler<TCommand> decoratee)
    {
        this.decoratee = decoratee;
    }

    public void Handle(TCommand command)
    {
        var stopwatch = Stopwatch.StartNew();
        this.decoratee.Handle(command);
        Debug.WriteLine(stopwatch.Elapsed);
    }
}

当然,只有在正确地将SOLID原则应用于您的设计时才能使用装饰器,因为您经常需要有一些明确的通用抽象,以便能够将装饰器应用于大范围的系统中的类。在传统的代码库中,我可能会有效地使用装饰器。