使用任务和Ninject进行并行化

本文关键字:并行化 Ninject 任务 | 更新日期: 2023-09-27 18:13:15

我有一个接口,我们称它为IConfig。接下来,我有一些类跑步者。Runner在其构造函数中接受一个IConfig和其他一些参数。

但是当我有多个IConfig实例,每个实例定义一个运行时,我不能get到跑步者,因为我得到一个ActivationException"超过一个匹配的绑定是可用的。"

我猜这是因为这不是标准内核期望的工作方式。我想要的是,内核为每个已绑定的IConfig创建一个新的Runner实例。

是否有一些方法可以使用ninject来做到这一点,而不必手动创建实例?

我想要的示例代码:

Task.WaitAll(kernel.GetAll<IRunner>().Select(r => Task.Run((Action)r.Run)).ToArray());

我必须做的代码示例:

IControl control = kernel.Get<IControl>();
Task[] runners = kernel.GetAll<IConfig>()
    .Select(c => new Runner(kernel.Get<IService1>(), kernel.Get<IService2>(), c, control))
    .Select(r => Task.Run((Action)r.Run))
    .ToArray();
Task.WaitAll(runners);

使用任务和Ninject进行并行化

对于这些场景没有开箱即用的支持。然而,有很多方法可以实现你想要的。

我们从

开始
public class Runner
{
    public Runner(IService1 service1, IService2 service2, IConfig config, IControl control)
    {
    }
    public void Run()
    {   
    }
}

解决方案1 :我们将使用.ToFactory()绑定来创建具有依赖注入特性的运行器。

public interface IRunnerFactory
{
    // all parameters are passed to the constructor of Runner
    // parameters are matched by name, so make sure they match! ctor(IConfig myconfig) won't work, it must be ctor(IConfig config).
    // but of course you can add more parameters to the ctor and have them injected: ctor(IService1 foo, IControl bar, IConfig config, IService2 foo2)
    Runner Create(IConfig config);
}
public class AllRunner
{
    private readonly IList<IConfig> runnerConfigurations;
    private readonly IRunnerFactory runnerFactory;
    public AllRunner(IList<IConfig> runnerConfigurations, IRunnerFactory runnerFactory)
    {
        this.runnerConfigurations = runnerConfigurations;
        this.runnerFactory = runnerFactory;
    }
    public void RunAll()
    {
        Task[] runners = this.runnerConfigurations
            .Select(this.runnerFactory.Create)
            .Select(runner => Task.Run((Action) runner.Run))
            .ToArray();
        Task.WaitAll(runners);
    }
}

// you could also use .InNamedScope() or maybe InParentScope() or InCallScope() -- see the NamedScope extension!
kernel.Bind<IControl>().To<Control>().InSingletonScope();
// implementation is auto generated.
kernel.Bind<IRunnerFactory>().ToFactory();
kernel.Bind<IConfig>().To<Config1>();
kernel.Bind<IConfig>().To<Config2>();
kernel.Bind<IConfig>().To<Config3>();

注意:

  • .ToFactory()需要Ninject.Extensions.Factory,也可以作为nuget包获得。

解决方案2

我们将扩展解决方案1并使用提供者/.ToProvider()绑定,以便我们可以注入IReadOnlyCollection<Runner>

public class RunnersProvider : Provider<IReadOnlyCollection<Runner>>
{
    private readonly IList<IConfig> runnerConfigurations;
    private readonly IRunnerFactory runnerFactory;
    public RunnersProvider(IList<IConfig> runnerConfigurations, IRunnerFactory runnerFactory)
    {
        this.runnerConfigurations = runnerConfigurations;
        this.runnerFactory = runnerFactory;
    }
    protected override IReadOnlyCollection<Runner> CreateInstance(IContext context)
    {
        return this.runnerConfigurations
            .Select(this.runnerFactory.Create)
            .ToArray();
    }
}

kernel.Bind<IReadOnlyCollection<Runner>>().ToProvider<RunnersProvider>();

你可以做

IResolutionRoot.Get<IReadOnlyCollection<Runner>>()

或者也可以将IReadOnlyCollection<Runner>注入到一个actor中,但不能将IEnumerable<Runner>或任何其他类型注入,因为您显式地绑定了IReadOnlyCollection<Runner>,而没有使用ninject的多绑定/多注入特性。

您还需要注意kernel.Bind<IEnumerable<Foo>>()(同样适用于IList<>, ICollection<>foo[] -所有类型的自动多注入支持)是脆弱的。你可以做kernel.Get<IEnumerable<Foo>>(),但你不能进行相同类型的ctor注入,因为如果你使用它进行ctor注入,ninject将尝试找到与Foo匹配的绑定,而不是与IEnumerable<Foo>(多注入支持)匹配的绑定。