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项目中使用了依赖注入技术来给程 ...
随机推荐
- CentOS以及Oracle数据库发展历史及各版本新功能介绍, 便于构造环境时有个对应关系
CentOS版本历史 版本 CentOS版本号有两个部分,一个主要版本和一个次要版本,主要和次要版本号分别对应于RHEL的主要版本与更新包,CentOS采取从RHEL的源代码包来构建.例如CentOS ...
- ugui,button的一个坑
如果ugui button下面挂了一些image或text,而这些image或text的范围超出了button本身的范围,则应将image和text的raycast Target属性的勾选去掉,否则只 ...
- treeGrid树形数据表格的json数据格式说明
在使用easyUI 的treeGrid的时候,很多时候我们从数据库取出来的数据treeGrid却不能读取显示成一个树:如下 { menuCode: "a00", menuName: ...
- VPS/服务器优化网络、加速方法总结与参考
在国外的服务器上因为受各种因素影响,即使国外的服务器都是百兆共享或者G口到国内下载速度都不是很让人满意,大部分人购买国外服务器是用作存储下载或者扶墙,速度慢的所以话影响我们的使用体验.所以就搞出了很多 ...
- Mysql与Oracle区别
1.Oracle是大型数据库而Mysql是中小型数据库,Oracle市场占有率达40%,Mysql只有20%左右,同时Mysql是开源的而Oracle价格非常高. 2.Oracle支持大并发,大访问量 ...
- CentOS MySQL 配置
问题: MySQL安装后root用户无法连接,提示 ERROR 1045 (28000): Access denied for user ’root’@’localhost’ (using passw ...
- Vim常用命令总结
Vim常用命令总结: 下文是小编自己整理的关于日常linux vim编程过程中用的一些常用命令,资料参考鸟哥的 如果有需要的小伙伴可以参考一下!(底部有下载地址) 一. 一般模式可用的按钮说明 1.1 ...
- WebService 错误:无法加载协定为xxx的终结点配置部分,因为找到了该协定的多个终结点配置
当在vs 2008中添加服务引用后,如果“更新”服务引用,或“删除”该服务引用后再次加入后,在运行时会出现此错误.这是因为在“更新/删除”服务引用时,app.config文件并不会自动修改,在“更新” ...
- ListView 中含有 EditText 导致焦点丢失的问题
ListView 中的 item 中有 EditText 时. 如果activity的输入法选项设置为 android:windowSoftInputMode="adjustResize&q ...
- 各公司年资金归集汇总sql
select bd_accid.accidcode, bd_accid.accidname, -- fts_voucher_b.interestdate, -- fts_voucher_b.summa ...