有时我们可以使用我们的DI容器做一些巧妙的技巧,例如:自动绑定,管理单例,管理每个请求的一个实例等。这很棒,并且可以真正简化某些场景。
我遇到的问题是,特定类的问题现在会泄漏到应用程序层。如果一个类期望以特定方式实例化和管理(例如,作为单个,或者每个http请求只执行一次),现在应该由应用程序层来确保这种情况发生。
发生的一些问题:
1)潜在的错误,因为应用程序可能错误地设置DI绑定。
2)当开发人员想要实现一个包时可能会造成混淆,因为设备DI容器的规则不是由包本身提供的,因此必须在注释或附带的测试用例中记录(不理想)。
3)如果类的实现发生了变化,现在每个应用程序都有责任使用该类来更新其DI容器绑定。
以下是一些使用NInject可以执行此操作的示例绑定:
public class MyApplicationsInjectionModule : NInjectModule
{
public void Load()
{
Bind<IFoo>().ToConstant(FooThatShouldBeASingleton.Instant);
Bind<IFoo>().To<FooThatShouldBeASingleton>().AsSingleton();
Bind<IFoo>().To<FooThatShouldOnlyBeInstantiatedOncePerRequest>().InRequestScope();
}
}
我的经验仅适用于NInject - 或许其他一些DI容器更优雅地处理这个问题。
在不放弃DI容器提供的功能的情况下,我们可以采取哪些策略来避免这些问题?
答案 0 :(得分:2)
依赖注入引入了灵活性,并且具有这种灵活性还会增加错误编写对象图的风险。这是DI的一个(非常)缺点之一,它与您是否使用DI容器无关。
1)潜在的错误,因为应用程序可能错误地设置DI绑定。
偶with Poor Man's DI it's perfectly possible to compose an incorrect object graph。
2)当开发人员想要实现一个包时可能会造成混淆,因为设备DI容器的规则不是由包本身提供的,因此必须在注释或附带的测试用例中记录(不理想)。
这就是为什么最好的策略(在.NET上)采用基于约定的配置。基本上,您可以告诉容器扫描所有适当的程序集,并根据它们实现的接口注册所有公共类。
如果您坚持构造函数注入,自动连接将为您完成剩下的工作。
举个例子,假设您在程序集中定义了这个类:
public class Foo : IFoo
{
private readonly IBar bar;
public Foo(IBar bar)
{
if (bar == null)
throw new ArgumentNullException("bar");
this.bar = bar;
}
// Use this.bar for something interesting in the class...
}
在另一个集会中,你可能有
public class Bar : IBar { }
配置为扫描适当程序集的容器将找到Bar
并将其注册为IBar
,并且会找到Foo
并将其注册到IFoo
。由于Bar
构造函数会静态地宣传其所需的依赖项,因此自动连接可以启动,容器将能够自动解析 IFoo 。
使用适当的约定,您无需重新配置容器即可向代码库添加新类型。
3)如果类的实现发生了变化,现在每个应用程序都有责任使用该类来更新其DI容器绑定。
同样,如果您使用约定优于配置,则会自动执行此操作。