两个注册之间运行时的简单注入器动态基于上下文的注入

Simple Injector dynamic context-based injection at runtime between two registrations

我有一个 Mediator 应用程序使用 Simple Injector 进行命令处理程序注册,并且注入和处理程序都已设置好并且运行良好。

class DoWashingCommandHandler : IRequestHandler<DoWashingCommand,object>
{
    IBus bus;

    public DoWashingCommandHandler(IBus bus) { this.bus = bus; }

    Task<object> Handle(DoWashingCommand command)
    {
        this.bus.Send(new OtheCommand());
    }
}

我需要 2 个 IBus 实现的注册。

第一个可以是任何生命周期,第二个有一个后台线程,所以我最初认为它需要是一个单例,但经过审查我相信它也可以是任何生命周期并且只有一个静态工作线程class 在其中(这对范围很重要):

// register as non-singleton to allow scope usage
// keep static worker thread as if it were Singleton
class DispatchOnBackgroundThread : IBus
{
    static Worker = new Worker();

    public Task<object> Send(object command)
    {
         Worker.Post(command);
    }

    public void Start(Container container, CancelationToken stoppingToken)
    {
         Worker.Start(container,stoppingToken);
    }

    class Worker
    {
         public void Post(object command) { /* snip */ }

         public void Start(Container container, CancelationToken stoppingToken)
         { /* snip */ }

         public void Thread()
         {
             /* loop */
             var item = ReadFromQueue();

             // get command handler type
             // get command handler instance from container
             // if instantiated instance has IBus dependency in this
             // section then it must have used DispatchInThread as the
             // concrete implementation for IBus (including if the handler
             // calls container.GetInstance<IBus>()

             handler.Handle(item.Request, cancellationToken);
         }

         // anything outside this Thread should use
         // DispatchOnBackgroundThread for IBus
    }
}

然后注册如下(不知道如何避免IBus的重复注册问题):

// i need to be able to register two types
container.Register<IBus,DispatchOnBackgroundThread>();
container.Register<IBus,DispatchInThread>();

// this would return any IBus references with DispatchOnBackgroundThread
var handler = this.container.GetInstance(requestHandlerType);

using(SomeSope.BeingScope(container))
{
    // this would return any IBus references with DispatchInThread
    var handler = this.container.GetInstance(requestHandlerType);
    // and if handler or other code referenced container and called
    // GetInstance, and IBus dependencies would be returned as 
    // DispatchInThread whilst in this scope
}

// this would return any IBus references with DispatchOnBackgroundThread
var other = this.container.GetInstance(requestHandlerType);

我认为,总而言之,这是 Context-based injection 和自定义范围的混合。

我怎样才能实现上述目标,或者这是一种可怕的代码味道?

为了在需要时提供进一步的上下文,我需要上述可切换的解析类型以实现另一个问题的解决方案

上面的DI代码去掉了link,但是我在实际实现中用的比较多的是SimpleInjector

我不确定我是否完全理解你的用例,以及导致这种情况的原因,但你可以做的是创建一个包装器 IBus 实现,将调用转发到正确的总线,在后台线程上 运行 更改转发总线实现时。

此包装器可能如下所示:

class SwitchableBus : IBus
{
    private readonly DispatchInCallingThread defaultBus;
    private readonly DispatchOnBackgroundThread backgroundBus;

    public SwitchableBus(
        DispatchInCallingThread defaultBus, DispatchOnBackgroundThread backgroundBus)
    {
        this.defaultBus = defaultBus;
        this.backgroundBus = backgroundBus;
        this.Bus = defaultBus;
    }

    public IBus Bus { get; private set; }

    public void SwitchToBackgroundBus() => this.Bus = this.backgroundBus;

    public Task<object> Send(object command) => this.Bus.Send(command);
}

有了这个包装器,您可以使用以下注册:

container.Register<IBus, SwitchableBus>(Lifestyle.Scoped);
container.Register<SwitchableBus>(Lifestyle.Scoped);
container.Register<DispatchInCallingThread>(Lifestyle.Scoped);
container.Register<DispatchOnBackgroundThread>(Lifestyle.Scoped);

这允许您在图表中使用 DispatchInCallingThread,如下所示:

using(SomeSope.BeingScope(container))
{
    var handler = this.container.GetInstance(requestHandlerType);
    handler.Handle(request);
}

换句话说,默认使用 DispatchInCallingThread

DispatchOnBackgroundThread可以被图形使用如下:

using(SomeSope.BeingScope(container))
{
    container.GetInstance<SwitchableBus>().SwitchToBackgroundBus();

    var handler = this.container.GetInstance(requestHandlerType);
    handler.Handle(request);
}

然而,这样做的结果是,您应该始终在活动范围内解决。但这无论如何都是一个好主意,因为无论如何图表中都可能存在 Scoped 依赖关系。 Simple Injector 不允许在活动范围的上下文之外解析具有 Scoped 依赖项的图形。