Ninject之旅之七:Ninject依赖注入
摘要
可以使用不同的模式向消费者类注入依赖项,向构造器里注入依赖项是其中一种。有一些遵循的模式用来注册依赖项,同时有一些需要避免的模式,因为他们经常导致不合乎需要的结果。这篇文章讲述那些跟Ninject功能相关的模式和反模式。然而,全面的介绍可以在Mark Seemann的书《Dependency Injection in .NET》中找到。
1、构造函数注入
构造函数时推荐的最常用的向一个类注册依赖项的模式。一般来说,这种模式应该经常被用作主要的注册模式,除非我们不得不使用其他的模式。在这个模式中,需要在构造函数中引入所有类依赖项列表。
问题是如果一个类有多于一个的构造函数会怎么样。尽管Ninject选择构造函数的策略是可以订制的,他默认的行为是选择那个有更多可以被Ninject解析的参数的构造函数。
因此在下面的例子中,尽管第二个构造函数有更多的参数,如果Ninject不能解析IService2,他将调用第一个构造函数。如果IService1也不能被解析,他将调用默认构造函数。如果两个依赖项都被注册了可以被解析,Ninject将调用第二个构造函数,因为他有更多的参数。
public class Consumer
{
private readonly IService1 dependency1;
private readonly IService2 dependency2;
public Consumer(IService1 dependency1)
{
this.dependency1 = dependency1;
}
9 public Consumer(IService1 dependency1, IService2 dependency2)
{
this.dependency1 = dependency1;
this.dependency2 = dependency2;
}
}
如果上一个类中有另一个构造函数也有两个可以被解析的参数,Ninject将抛出一个ActivationException异常,通知多个构造函数有相同的优先级。
有两种方式可以重载默认的行为,显示地选择调用哪一个构造函数。第一种方式是在绑定中指出需要的构造函数:
Bind<Consumer>().ToConstructor(arg => new Consumer(arg.Inject<IService1>()));
另一种方式是在需要的构造函数中使用[Inject]特性:
[Inject]
public Consumer(IService1 dependency1)
{
this.dependency1 = dependency1;
}
在上面的例子中,我们再第一个构造函数中使用了[Inject]特性,显式地指定在初始化类中注入依赖项时调用的构造函数。尽管第二个构造函数有更多的参数,按照Ninject默认的策略会选择第二个构造函数。
注意在多个构造函数中同时使用这个特性时会产生ActivationException异常。
2、初始化方法和属性注入
除了构造函数注入之外,Ninject支持通过初始化方法和属性setter依赖注入。我们可以通过[Inject]特性指定任意多的需要的方法和属性来注入依赖项。
尽管依赖项在类一初始化的时候就被注入,但是不能预计依赖项注入的顺序。下面的例子演示如何指定一个属性的注入:
[Inject]
public IService Service
{
get { return dependency; }
set { dependency = value; }
}
下面的例子使用注入方法注册依赖项:
[Inject]
public void Setup(IService dependency)
{
this.dependency = dependency;
}
注意只有公有成员和公有构造函数才可以被注入,甚至internal的成员都被忽视除非Ninject配置成可以注册非公有成员。
在构造函数注入中,构造函数是单一的点,在这个点上,类被初始化后就可以使用它的所有的依赖项。但是,如果我们使用初始化方法,依赖项通过多个点以无法预期的顺序被注入。因此,不能知道在哪个方法中,所有的依赖项都已经被注入可以使用了。为了解决这个问题,Ninject提供了IInitializable接口。这个接口有一个IInitialize方法,一旦所有的依赖项都被注入,将调用这个方法:
public class Consumer : IInitializable
{
private IService1 dependency1;
private IService2 dependency2;
[Inject]
public IService Service1
{
get { return dependency1; }
set { dependency1 = value; }
}
[Inject]
public IService Service2
{
get { return dependency2; }
set { dependency2 = value; }
}
public void Initialize()
{
// Consume all dependencies here
}
}
尽管Ninject支持使用属性和方法注入,构造函数注入应该是优先的方式。首先,构造函数注入使类更好的重用性,因为所有的类依赖项的列表是可见的。在初始化属性或方法里,类的使用者需要研究类的所有的成员或者浏览了类说明文档后(如果有的话),才能发现他的依赖项。
当使用构造函数注入的时候,类的初始化更容易。因为所有的依赖项在同一时刻被注入,我们可以很容易地在相同的地方使用这些依赖项。正如我们在前面的例子中看到的那样,在构造函数注入的场景中,注入的字段可能是只读的。因为只读字段只能在构造函数中被初始化,我们需要将他改成可写的,才可以使用初始化方法和属性对他进行注入。这将导致潜在的修改字段可读写属性的问题。
3、服务定位器
服务定位器是Martin Fowler介绍的一种很有争议的设计模式。尽管在一些特定的场景中可能有用,他一般被认为是一种反模式,需要尽可能避免。如果我们不属性这个模式,Ninject很容易地被误用成服务定位器。下面的例子示范误用Ninject kernal成一个服务定位器而不是一个DI容器:
public class Consumer
{
public void Consume()
{
var kernel = new StandardKernel();
var depenency1 = kernel.Get<IService1>();
var depenency2 = kernel.Get<IService2>();
...
}
}
前面的代码有两个重大的缺点。第一个是尽管我们使用一个DI容器,但是我们不可能一直使用DI。这个类跟Ninject kernal绑在一起,然而Ninject kernal并不真的是这个类的依赖项。这个类以及他所有预期的调用类将总是不必要的依赖于kernal对象和Ninject类库。在另一方面,真正的类依赖项(IService1和IService2)对于这个类却不可见,降低了可重用性。即使我们按照下面的方式修改这个类的设计,这个问题仍然存在:
public class Consumer
{
private readonly IKernel kernel;
public Consumer(IKernel kernel)
{
this.kernel = kernel;
}
public void Consume()
{
var depenency1 = kernel.Get<IService1>();
var depenency2 = kernel.Get<IService2>();
...
}
}
前面的类仍旧依赖于Ninject类库,然后他不必要这样,他真正的依赖项仍然对调用者不可见。可以很容易地使用构造函数注入模式重构:
public Consumer(IService1 dependency1, IService2 dependency2)
{
this.dependency1 = dependency1;
this.dependency2 = dependency2;
}
Ninject之旅之七:Ninject依赖注入的更多相关文章
- (依赖注入框架:Ninject ) 一 手写依赖注入
什么是依赖注入? 这里有一个场景:战士拿着刀去战斗: 刀: class Sword { public void Hit(string target) { Console.WriteLine($&quo ...
- Ninject之旅目录
第一章:理解依赖注入 Ninject之旅之一:理解DI 第二章:开始使用Ninject Ninject之旅之二:开始使用Ninject(附程序下载) Ninject之旅之三:Ninject对象生命周期 ...
- 6.在MVC中使用泛型仓储模式和依赖注入实现增删查改
原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository-pat ...
- 在MVC中使用泛型仓储模式和依赖注入实现增删查改
标签: 原文链接:http://www.c-sharpcorner.com/UploadFile/3d39b4/crud-operations-using-the-generic-repository ...
- [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject
本人博客已转移至:http://www.exblr.com/liam 为什么需要依赖注入 在[ASP.NET MVC 小牛之路]系列的理解MVC模式文章中,我们提到MVC的一个重要特征是关注点分离( ...
- 依赖注入(DI)和Ninject,Ninject
我们所需要的是,在一个类内部,不通过创建对象的实例而能够获得某个实现了公开接口的对象的引用.这种“需要”,就称为DI(依赖注入,Dependency Injection),和所谓的IoC(控制反转,I ...
- 使用Ninject进行DI(依赖注入)
Ninject是一个快如闪电.超轻量级的基于.Net平台的依赖注入框架.它能够帮助你把应用程序分离成一个个松耦合.高内聚的模块,然后用一种灵活的方式组装起来.通过使用Ninject配套你的软件架构,那 ...
- Ninject依赖注入——构造函数的注入
1.Ninject简介 Ninject是基于.Net平台的依赖注入框架,它能够将应用程序分离成一个个高内聚.低耦合(loosely-coupled, highly-cohesive)的模块,然后以一种 ...
- <Pro .NET MVC4> 三大工具之依赖注入神器——Ninject
这篇内容是对<Pro .NET MVC4>一书中关于Ninject介绍的总结. Ninject是.NET MVC的一款开源的依赖注入工具. 使用场景:当MVC项目中使用了依赖注入技术来给程 ...
随机推荐
- javaWeb——图片验证
publicvoid doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, ...
- undefined reference to `dlopen'
g++ -O0 -g3 -I. -Ithird/json -Ithird/core/include -Ithird/vite/include -Ithird/openfst-1.2.10/src/in ...
- 安卓,支付宝app登录时,提示 服务器安全证书已过期或不可信任,请问怎么解决
安卓,支付宝app登录时,提示 服务器安全证书已过期或不可信任,请问怎么解决 请把手机时间调成当前时间.
- 原生js事件委托
var commontop = document.getElementById("commontop");commontop.onclick = function(ev){ v ...
- Beta版本冲刺——day3
No Bug 031402401鲍亮 031402402曹鑫杰 031402403常松 031402412林淋 031402418汪培侨 031402426许秋鑫 站立式会议 大将回归,基本功能接口也 ...
- arcgis 10.2连接Oracle
- 根据Request获取客户端IP 内网IP及外网IP
在JSP里,获取客户端的IP地址的方法是:request.getRemoteAddr() ,这种方法在大部分情况下都是有效的.但是在通过了Apache,Squid等反向代理软件就不能获取到客户端的真实 ...
- <<redis设计和实现>>读书笔记
redis如何实现主从同步的高效率?? 主从复制的同步有一个命令数据的同步文本,然后利用两个不同服务器的偏移量来进行进行同步,避免每次都是全部同步(并非会保存所有的命令数据,而是会有一个缓冲区(比如1 ...
- sql server 去除(替换)空格,回车,换行 函数
--create-- SQL去除回车符,换行符,空格和水平制表符create function RepSymbolChar(@str nvarchar(max))returns nvarchar(ma ...
- Jmeter测试数据库
1.创建线程组 2.右键 Thread Group -> add ConfigElement -> JDBC Connection Configuration创建一个JDBC配置 (这有个 ...