使用Ninject(或任何其他IoC容器)创建一个默认绑定,以便在不存在适当的实现时使用此默认绑定,而不是在请求特定绑定时处理
我一直在使用Ninject的Factory和Conventions扩展项目,但我想知道它们是否掩盖了我在更基本层面上犯的错误,因此我创建了一个测试,尽可能简单地说明我想做的事情:
给定以下内容:
我不确定这样做是否可行,如果可以的话,如何去实现服务定位器模式实现不好,但其他人发表了类似问题的答案,也对此提出了警告。
那么,我所要求的是滥用/误用IoC容器吗?
对于所有已知类型,我都应该使用IoC来绑定/解析类型,所以这一部分看起来没有问题。在实际情况中,像BlueWidget这样的显式绑定会有很多种,并且“RedWidget”字符串值的变化数量也不确定。
总的来说,似乎默认实现某些接口的概念并不少见,那么,如果不在IoC容器的范围内,那么解决请求的机制会在哪里呢?
我还计划使用工厂模式来创建IWidget实现。到目前为止,我一直在使用由Ninject.Extensions.Factory自动创建的工厂,配合自定义实例提供程序和自定义绑定生成器,但是我无法解决这个问题。
使用自己的工厂实现是否有助于更好地控制(换句话说,使用我自己的工厂而不是来自Ninject.Extensions.Factory的自动工厂)?过去,我曾经使用程序集反射找到候选类型,并使用Activation.CreateInstance()来创建我需要的特定实现或默认实现,但是一旦这些实现采用面向依赖注入的原则,它们就具有自己的构造函数注入依赖项,因此这变得非常麻烦。因此,我转而使用IoC容器来解决问题-但这并不像我希望的那样奏效。
更新1——使用我的工厂实现成功
我其实对此不太满意,因为每次编写新的IWidget实现时,我都必须打开这个工厂并进行更新。在我的示例中,我还必须在绑定中添加另一行-但是这就需要使用基于约定的绑定,我计划使用该方法以避免不断更新绑定定义。
使用这个工厂实现:
ActivationException
,这是否可行或是个好主意?我一直在使用Ninject的Factory和Conventions扩展项目,但我想知道它们是否掩盖了我在更基本层面上犯的错误,因此我创建了一个测试,尽可能简单地说明我想做的事情:
给定以下内容:
public interface IWidget { }
public class DefaultWidget : IWidget { }
public class BlueWidget : IWidget { }
以下是使用FluentAssertions的xUnit测试示例:
[Fact]
public void Unknown_Type_Names_Resolve_To_A_Default_Type()
{
StandardKernel kernel = new StandardKernel();
// intention: resolve a `DefaultWidget` implementation whenever the
// 'name' parameter does not match the name of any other bound implementation
kernel.Bind<IWidget>().To<DefaultWidget>();
kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof(BlueWidget).Name);
kernel.Get<IWidget>("RedWidget").Should().BeOfType<DefaultWidget>();
// ACTIVATION EXCEPTION (**NO matching bindings available** for `IWidget`)
}
我不确定这样做是否可行,如果可以的话,如何去实现服务定位器模式实现不好,但其他人发表了类似问题的答案,也对此提出了警告。
那么,我所要求的是滥用/误用IoC容器吗?
对于所有已知类型,我都应该使用IoC来绑定/解析类型,所以这一部分看起来没有问题。在实际情况中,像BlueWidget这样的显式绑定会有很多种,并且“RedWidget”字符串值的变化数量也不确定。
总的来说,似乎默认实现某些接口的概念并不少见,那么,如果不在IoC容器的范围内,那么解决请求的机制会在哪里呢?
我还计划使用工厂模式来创建IWidget实现。到目前为止,我一直在使用由Ninject.Extensions.Factory自动创建的工厂,配合自定义实例提供程序和自定义绑定生成器,但是我无法解决这个问题。
使用自己的工厂实现是否有助于更好地控制(换句话说,使用我自己的工厂而不是来自Ninject.Extensions.Factory的自动工厂)?过去,我曾经使用程序集反射找到候选类型,并使用Activation.CreateInstance()来创建我需要的特定实现或默认实现,但是一旦这些实现采用面向依赖注入的原则,它们就具有自己的构造函数注入依赖项,因此这变得非常麻烦。因此,我转而使用IoC容器来解决问题-但这并不像我希望的那样奏效。
更新1——使用我的工厂实现成功
我其实对此不太满意,因为每次编写新的IWidget实现时,我都必须打开这个工厂并进行更新。在我的示例中,我还必须在绑定中添加另一行-但是这就需要使用基于约定的绑定,我计划使用该方法以避免不断更新绑定定义。
使用这个工厂实现:
public interface IWidgetFactory { IWidget Create(string name); }
public class WidgetFactory : IWidgetFactory
{
private readonly IKernel kernel;
public WidgetFactory(IKernel kernel) { this.kernel = kernel; }
public IWidget Create(string name)
{
switch (name)
{
case "Blue":
return this.kernel.Get<IWidget>(typeof (BlueWidget).Name);
default:
return this.kernel.Get<IWidget>(typeof (DefaultWidget).Name);
}
}
}
我可以使这个测试通过:
[Fact]
public void WidgetBuilders_And_Customizers_And_Bindings_Oh_My()
{
StandardKernel kernel = new StandardKernel();
kernel.Bind<IWidget>().To<DefaultWidget>().Named(typeof(DefaultWidget).Name);
kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof (BlueWidget).Name);
kernel.Bind<IWidgetFactory>().To<WidgetFactory>().InSingletonScope();
kernel.Get<IWidgetFactory>().Create("Blue")
.Should().BeOfType<BlueWidget>();
kernel.Get<IWidgetFactory>().Create("Red")
.Should().BeOfType<DefaultWidget>();
}
它能够正常工作,但是感觉不对,原因如下:
- 我必须将
IKernel
注入到IWidgetFactory
中 - 对于每个新的
IWidget
实现,都必须更新IWidgetFactory
- 似乎应该已经为这种情况创建了 Ninject 扩展
更新1结束
在这种情况下,你会怎么做,考虑到 IWidget
实现的数量很高,"widget name" 参数的预期范围基本上是无限的,并且所有无法解析的 widget 名称都应该使用 DefaultWidget
处理?
如果您有兴趣,可以继续阅读以下各种测试的完整演变过程:
[Fact]
public void Unknown_Type_Names_Resolve_To_A_Default_Type()
{
StandardKernel kernel = new StandardKernel();
// evolution #1: simple as possible
// PASSES (as you would expect)
//kernel.Bind<IWidget>().To<BlueWidget>();
//kernel.Get<IWidget>().Should().BeOfType<BlueWidget>();
// evolution #2: make the only binding to a different IWidget
// FAILS (as you would expect)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Get<IWidget>().Should().BeOfType<BlueWidget>();
// evolution #3: add the binding to `BlueWidget` back
// ACTIVATION EXCEPTION (more than one binding for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>();
//kernel.Get<IWidget>().Should().BeOfType<BlueWidget>();
// evolution #4: make `BlueWidget` binding a *named binding*
// ACTIVATION EXCEPTION (more than one binding for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof (BlueWidget).Name);
//kernel.Get<IWidget>().Should().BeOfType<BlueWidget>();
// evolution #5: change `Get<>` request to specifiy widget name
// PASSES (yee-haw!)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof(BlueWidget).Name);
//kernel.Get<IWidget>("BlueWidget").Should().BeOfType<BlueWidget>();
// evolution #6: make `BlueWidget` binding *non-named*
// ACTIVATION EXCEPTION (**NO matching bindings available** for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>();
//kernel.Get<IWidget>("BlueWidget").Should().BeOfType<BlueWidget>();
// evolution #7: ask for non-existance `RedWidget`, hope for `DefaultWidget`
// ACTIVATION EXCEPTION (**NO matching bindings available** for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>();
//kernel.Get<IWidget>("RedWidget").Should().BeOfType<DefaultWidget>();
// evolution #8: make `BlueWidget` binding a *named binding* again
// ACTIVATION EXCEPTION (**NO matching bindings available** for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof(BlueWidget).Name);
//kernel.Get<IWidget>("RedWidget").Should().BeOfType<DefaultWidget>();
// evolution #9: remove `RedWidget` specification in Get<> request
// ACTIVATION EXCEPTION (back to **more than one** binding for `IWidget`)
//kernel.Bind<IWidget>().To<DefaultWidget>();
//kernel.Bind<IWidget>().To<BlueWidget>().Named(typeof(BlueWidget).Name);
//kernel.Get<IWidget>().Should().BeOfType<DefaultWidget>();
}
.Binding.IsImplicit
,这似乎不再存在。我在SO上寻找任何包括“Ninject Default Fallback Binding”等内容的东西,但没有什么有用的结果。我在程序员stackexchange上提出了一个更抽象的问题,看看这是否是一个从一开始就不好的想法。 - Jeff