我正在使用Ninject创建一组"插件",例如我有:
Bind<IFoo>().To<Class1>();
Bind<IFoo>().To<Class2>();
Bind<IFoo>().To<Class3>();
...稍后我使用kernel.GetAll<IFoo>()
并迭代结果。当然,Class1/Class2/Class3
中的每一个都实现IFoo
,并且具有由Ninject注入的一堆参数的构造函数,例如Class1
的构造函数是 public Class1(IBar bar, IBaz baz)
< / strong>,Ninject注入了IBar
和IBaz
。到现在为止还挺好。
然而,现在我想要两个不同的版本&#34; Class1
的{{1}},都绑定到IFoo
,仅在构造时传递的值不同。也就是说,例如,假设Class1
构造函数现在是 public Class1(IBar bar, IBaz baz, bool myParameter)
,我想执行以下操作:
Bind<IFoo>().To<Class1>(); //Somehow pass 'true' to myParameter here
Bind<IFoo>().To<Class1>(); //Somehow pass 'false' to myParameter here
Bind<IFoo>().To<Class2>();
Bind<IFoo>().To<Class3>();
...然后,当我致电kernel.GetAll<IFoo>()
时,我想要返回IFoo
的4个版本(Class1
&#34; true&#34;版本,Class1
false版本,Class2
和Class3
)。 我已阅读Ninject文档但无法找到实现此目的的方法。
以下是我尝试过的一些想法,但没有一个能够很好地运作:
1)我可以将类(例如Class1True
和Class1False
)分开,一个派生自另一个,并绑定到它们。问题是,当我必须为许多类做这个时,这个解决方案并没有真正扩展 - 我最终用很多无用的类来污染我的类层次结构,当我想通过构造函数参数时问题变得更糟比布尔更复杂。现实的例子:
Bind<IDrawingTool>().To<Brush>(); //Somehow pass '5' to brushThickness to create a fine brush
Bind<IDrawingTool>().To<Brush>(); //Somehow pass '25' to brushThickness to create a medium brush
Bind<IDrawingTool>().To<Brush>(); //Somehow pass '50' to brushThickness to create a coarse brush
Bind<IDrawingTool>().To<Pencil>();
Bind<IDrawingTool>().To<SprayCan>();
当然,这只是无限多种可能配置的一种可能配置。为每个画笔厚度创建一个新类似乎是错误的。
2)我研究了使用.ToMethod绑定的可能性,如下所示:
Bind<IDrawingTool>().ToMethod(c => new Brush(5));
Bind<IDrawingTool>().ToMethod(c => new Brush(25));
Bind<IDrawingTool>().ToMethod(c => new Pencil());
但在这种情况下,我对以下内容感到困惑:
a)如果Brush()构造函数实际上还需要其他参数,必须通过Ninject注入,该怎么办?
b)实际上是否允许多个ToMethod绑定?
c)这适用于InSingletonScope()吗?
总结一下:什么是绑定多个&#34;版本的好方法&#34;相同类型?
答案 0 :(得分:2)
如果您不使用任何条件绑定,请解决这些&#34;多个版本&#34;在运行时,容器检测到依赖关系将导致异常,由于歧义。它肯定可以在基于服务定位器的访问中工作,但是在组成对象图的真正的DI中,使用这种方法会遇到麻烦。
在您描绘的场景中,如果存在以下假设情况,则会出现这种模糊性:
public class MyDraw
{
public MyDraw(IDrawingTool drawingTool)
{
// Your code here
}
}
kernel.Bind<IDrawingTool>().ToMethod(c => new Brush(5));
kernel.Bind<IDrawingTool>().ToMethod(c => new Brush(25));
kernel.Bind<IDrawingTool>().ToMethod(c => new Pencil);
// Runtime exception due to ambiguity: How would the container know which drawing tool to use?
var md = container.Get<MyDraw>();
但是,如果要注入此类:
public class MyDraw
{
public MyDraw(IEnumerable<IDrawingTool> allTools)
{
// Your code here
}
}
由于多次注射,这将起作用。 caontainer只会调用匹配IDrawingTool
的所有绑定。在这种情况下,允许多个绑定,甚至ToMethod(...)
个。
您需要做的是依靠命名绑定或上下文绑定(使用WhenXXX(...)
语法等机制来让目标< / strong>注入以确定它需要哪种具体实现.Ninject对此有广泛的支持,实际上是它的核心DI框架的定义特征之一。你可以阅读它here。
答案 1 :(得分:2)
为同一类型创建两个绑定完全没问题,这两个绑定仅在参数方面有所不同。 所以你要做的是:
Bind<IFoo>().To<Class1>().WithConstructorArgument("boolParameterName", true);
Bind<IFoo>().To<Class1>().WithConstructorArgument("boolParameterName", false);
使用WithConstructorArgument
传递参数。您可以通过名称使Ninject与参数匹配 - 在上面的示例中,Class1
ctor需要具有名称正好为boolParameterName
的bool参数。或者您可以匹配类型,在这种情况下,您只能在构造函数中具有该类型的一个参数。示例:WithConstructorArgument(typeof(bool), true)
。
您不需要WithConstructorArgument
指定的所有参数都可以像往常一样使用ctor-inject&#34;
完整的工作示例(使用xunit和FluentAssertions nuget包):
public interface IBar { }
public class Bar : IBar { }
public interface IFoo { }
class Foo1 : IFoo
{
public Foo1(IBar bar) { }
}
class Foo2 : IFoo
{
public Foo2(IBar bar, bool theParametersName) { }
}
[Fact]
public void FactMethodName()
{
var kernel = new StandardKernel();
kernel.Bind<IBar>().To<Bar>();
kernel.Bind<IFoo>().To<Foo1>();
kernel.Bind<IFoo>().To<Foo2>().WithConstructorArgument("theParametersName", true);
kernel.Bind<IFoo>().To<Foo2>().WithConstructorArgument("theParametersName", false);
List<IFoo> foos = kernel.GetAll<IFoo>().ToList();
foos.Should().HaveCount(3);
}