我有一个接口,我们称它为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);
对于这些场景没有开箱即用的支持。然而,有很多方法可以实现你想要的。
我们从
开始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>
(多注入支持)匹配的绑定。