《Pro ASP.NET MVC 3 Framework》学习笔记之五【依赖注入及ninject工具使用】

一,创建松耦合的组件

1."分解关注点"是MVC模式里面一个非常重要的特性。我们想要在应用程序里面创建的组件尽可能的独立,这样我们就能管理比较少的依赖关系。理想情况下,每个组件都是孤立的,不知道其他组件的存在,处理应用程序的其他领域仅仅通过抽象接口,这就是所谓的松耦合,它让我们的应用程序更加容易测试和修改。通过一个简单的例子可以帮助我们理解,假如我们想写一个发邮件的组件,暂且就把这个组件命名为MyEmailSender,接着我们实现一个接口,这个接口定义了所有需要发送邮件的功能,也暂且将这个接口命名为IEmailSender。任何其他的应用程序的组件需要引用IEmailSender里面的方法就行了。

比如有一个重置密码的组件PasswordResetHelper需要在用户重置密码后发生邮件,下图展示这它们之间的关系:


通过引入IEmailSender,我们就能够确保在PasswordResetHelper跟MyEmailSender之间没有直接的依赖关系。比如,我们可以用其他的实现了发送邮件的Provider来替换当前的MyEmailSender而不会对PasswordResetHelper造成影响,从这里也能够体会到松耦合的好处吧。

当然并不是所有的组件之间存在的关系都需要用接口来解耦和。这取决于我们的应用程序的复杂程度,需要什么样的测试,长期维护的可能性。我们不用去对一个简单的ASP.NET MVC程序执行解耦。

2.使用依赖注入

接口能够帮助我们解耦组件,但是这样仍然面临一个问题,那就是C#并没有提供一种嵌入的方式来比较容易的创建实现接口的对象,因为我们只能创建一个具体实现了接口的组件的实例,比如这里的的MyEmailSender的实例。像下面的这种方式:

public class PasswordResetHelper {public void ResetPassword()         {               IEmailSender mySender = new MyEmailSender();//一些关于邮件的详细               mySender.SendEmail();        }}

我们仅仅做了松耦合的一部分工作,PasswordResetHelper类通过IEmailSender来配置和发送邮件,通过接口的实现来创建对象,这里需要创建一个MyEmailSender的实例。这样看来,我们可能让事情更糟,因为现在的PasswordResetHelper同时依赖IEmailSender和MyEmailSender,正如下图所显示的那样:


其实我现在需要一种方式来获取对象(这里就是指上面代码里的mySender),这个对象是实现了我们给定的接口但不是直接去创建实现接口(这里指MyEmailSender)对象本身。对于这个问题的解决方案,我们称为依赖注入(dependency injection(DI)),也可以被认为是控制反转(inversion of control(IoC))。

3.DI(dependency injection):

一种完成松耦合的设计模式,这是一个非常重要的概念,它是高效MVC开发的中心。
DI分为两个部分:一是从我们的组件里面移除任何的对具体类的依赖性。在我们的这个例子里面,我们这样来做,将对必要接口的实现移动到类的构造器里面,如下所示:

public class PasswordResetHelper{private IEmailSender emailSender;public PasswordResetHelper(IEmailSender emailSenderParam)       {           emailSender = emailSenderParam;      }public void ResetPassword()      {//邮件的详细配置...          emailSender.SendEmail();      }}

这样做了以后,我们可以发现,没有了MyEmailSender,这也就意味着我们打破了PasswordResetHelper和MyEmailSender之间的依赖性。PasswordResetHelper的构造器需要一个对象作参数,而这个对象是IEmailSender接口的的实现,它不用去知道这个对象是什么,或者说它根本不用去关心,并且也不用负责去创建它。

4.通过上面的操作,依赖在运行时就被注入到了PasswordResetHelper里面,也意味着那些实现了IEmailSender接口的类的实例将会被创建,并在PasswordResetHelper实例化期间传递给它的构造器。这样在PasswordResetHelper和任何实现了它需要的接口的类之间没有编译时的依赖。

PasswordResetHelper是通过它的构造器来实现依赖注入的,我们把这种称为Constructor Injection(构造器注入);我也可以通过它的公共属性来实现依赖注入,通常称这种方式为Setter Injection(设置注入)。如果你想了解 Constructor Injection vs. Setter Injection,可以猛击这里

因为依赖是在运行时处理,这样我们就可以决定在程序运行的时候使用哪个接口实现。就像这里我们可以不同的Email Provider,或者是伪造一个仅仅用来测试。通过上面的方式我们目的就达到了。

二,一个具体的MVC 依赖注入的例子

还是回到我之前做的竞拍,接下就是将依赖注入应用到我们的竞拍程序里面,我们的目标很简单,创建一个controller命名为AdminController,我们使用MembersRepository来持久化,为了解决AdminController和MembersRepository之间的耦合,我们定义一个接口IMembersRepository.具体的代码如下:

public interface IMembersRepository{void AddMember(Member member);      Member FetchByLoginName(string loginName);void SubmitChanges();}public class MembersRepository : IMembersRepository {public void AddMember(Member member) {...}public Member FetchByLoginName(string loginName) {... }public void SubmitChanges() {...}}

现在我写个controller类,它依赖于IMembersRepository,如下所示:

public class AdminController : Controller {     IMembersRepository membersRepository;     public AdminController(IMembersRepository repositoryParam)      {         membersRepository = repositoryParam;     }     public ActionResult ChangeLoginName(string oldLoginParam, string newLoginParam)      {         Member member = membersRepository.FetchByLoginName(oldLoginParam);         member.LoginName = newLoginParam;         membersRepository.SubmitChanges();         //用来呈现一些View     }}

AdminController需要一个IMembersRepository接口的实现的对象作为构造器的参数。这个在运行时会被注入,也会允许AdminController对接口实现的对象的实例进行操作而不会发生耦合。

使用一个依赖注入容器

我已经解决了依赖的问题,因为我们会在程序运行时注入构造器依赖,

但是我仍然需要去解决另一个问题:我们怎样去实例化接口实现对象的实例而不在我们程序的其他地方创建依赖。

解决的办法就是DI容器,或者也称为IoC 容器。它实际上是一个组件,充当着依赖与实现依赖之间的"经纪人"的角色,我理解为就是中介代理什么的。具体到我们的例子里面就是,PasswordResetHelper需求(Demands)跟MyEmailSender之间的"经纪人"。

我们通过DI容器注册应用程序的使用的接口或抽象类的集合,这样就是要告诉DI容器选择哪一个合适具体类的实例来满足依赖。所以我们也应该通过容器注册IEmailSender,并且具体到当IEmailSender的实现被需要时,哪一个MyEmailSender的实例被创建,无论我们什么时候需要调用IEmailSender,比如创建一个PasswordResetHelper的实例,我们就去DI容器,它会给我们一个之前注册的作为默认的(接口实现类)的实例,这里可能比较绕,我们的例子里面指的就是MyEmailSender

可能你跟我一样,对DI容器还非常陌生,它到底是个什么东西。呵呵,不用担心,我们不用去实现DI容器,有很多好的开源的实现可以直接用。其中有一个Ninject推荐使用,可能你又跟我一样对Ninject同样陌生,也不用担心,你可以猛击这里。当然后面也会有专门的章节介绍它,所以我们大可放心。当然微软也创建了自己的DI容器Unity,如果你想了解,也可以猛击这里

我们可能会觉得DI的角色不重要,那你就错了,其实好的DI会具备一些非常"聪明"的特性,如依赖链的解决,对象生命周期的管理,构造器参数的配置等。并不建议我们自己建DI容器,如果是自己做试验可以,因为Ninject已经做的很好了,而且也推荐使用。

今天的笔记做到这里,到这里已经完成了前四章的笔记,也就是说我才学习到这个地方,非常希望路过的大牛多指导。

我也是刚学习MVC,做笔记是为了巩固和加深理解,当然如果能给到那些跟我一样的初学者一点点的帮助,我就非常高兴了。笔记里面肯定会有我理解不对的地方,还请路过的大牛们多多帮助指导。谢谢!
祝路过的朋友工作顺利!

晚安!

时间: 2024-07-28 14:27:58

《Pro ASP.NET MVC 3 Framework》学习笔记之五【依赖注入及ninject工具使用】的相关文章

ASP.NET MVC 3 Framework学习笔记之Model Templates

.使用模板化的视图Helpers(Using Templated View Helpers) 模版化视图helpers的创意就是它们更加灵活.我们不用自己去指定应该用什么HTML元素来呈现一个模型的属性,MVC自己会搞定,在我们更新了视图模型时,也不用手动的更新视图.下面是一个例子:  代码如下 复制代码 //在Models里面添加Persons.cs using System; using System.Collections.Generic; using System.Linq; using

《Pro ASP.NET MVC 3 Framework》学习笔记目录

<Pro ASP.NET MVC 3 Framework>简介: 作者: Adam Freeman 和 Steven Sanderson 出版社: Apress; New 平装: 820页 语种: 英语 ISBN: 1430234040 声明:笔记里面按我自己的理解翻译了大部分内容,写这个笔记的目的:为了方便自己查阅,也为园友提供学习的方便. 我无意侵犯作者的任何权利,仅仅为了自己学习.也希望路过的朋友不要用于任何商业目的. 第一部分 ASP.NET MVC3介绍   <Pro ASP.

《Pro ASP.NET MVC 3 Framework》学习笔记之一【MVC的历程,优点,HelloWorld】

序论:asp.net mvc出现已经有两三年的时间了(2009开始1.0版本),但是这么方面的中文学习资料仍然非常少,特别是asp.net mvc3,几乎就没有中文的学习书籍.在英文的书籍中有两本是非常经典的mvc3教程:<Professional ASP.NET MVC 3>--作者:Jon Galloway , Phil Haack, Brad Wilson , K. Scott Allen和<Pro ASP.NET MVC 3 Framework>--作者:Steven Sa

ASP.NET MVC Web API 学习笔记----HttpClient简介

  1. HttpClient简单介绍  依稀还记得那个时候用WebClient,HttpWebRequest来发送一个请求,现在ASP.NET MVC4中自带了一个类HttpClient,用于接收HttpResponseMessage和发送HttpRequestMesssage. 问题在于既然WebClient,HttpWebRequest可以完成相应的功能,为什么还要使用HttpClient类,.NET Framework中既然提出了这样一个类肯定是有其特别之处的,这里罗列几个不同之处: (

ASP.NET MVC Web API 学习笔记---联系人增删改查

本章节简单介绍一下使用ASP.NET MVC Web API 做增删改查.目前很多Http服务还是通过REST或者类似RESP的模型来进行数据操作的.下面我们通过创建一个简单的Web API来管理联系人          说明:为了方便数据不使用真正的数据库,而是通过内存数据模拟    1.       Web API中包含的方法 Action HTTP method Relative URI GetAllContact GET /api/contact GetContact GET /api/

《Pro ASP.NET MVC 3 Framework》学习笔记之九【Ninject的使用-下】

接着上次的Ninject的笔记,如果你是初次路过,可以先看看我前面的笔记. 一,创建依赖链(Chains of Dependency) 当我们向Ninject请求创建一个类型时,Ninject会去检查该类型和其他类型之间的耦合关系.如果有额外的依赖,Ninject也会解析它们并创建我们需要的所有类的实例.为了进一步说明,我们创建一个新的接口和一个实现该接口的类.请注意我们的例子是跟前面的笔记衔接的,所以如果你打算跟着一起操作的话,最好能够去看看前面的笔记. 创建一个IDiscountHelper

《Pro ASP.NET MVC 3 Framework》学习笔记之四【领域模型介绍】

主题:应用领域驱动开发(Applying Domain-Driven Development) Domain Model是MVC程序的"心脏",其他的一切,包括Controllers和Views仅仅是用来跟Domain Model交互的一种方式,ASP.NET MVC并没有限制使用在Domain Model上面的技术,我们可以自由的选择跟.net framework交互的技术,并且这样的选择是非常多的.不仅如此,ASP.NET MVC为我们提供了基础的架构和约定来帮助Domain Mo

《Pro ASP.NET MVC 3 Framework》学习笔记之八【Ninject的使用-上】

本次的笔记分为三个部分:Ninject(依赖注入容器,前面有介绍的,如果你第一次路过这里,可以先看下我前面的笔记),NUnit(单元测试工具),Moq(用来模拟在单元测试中的接口实现).今天我做的笔记是关于第一部分:Ninject. 如果你对依赖注入(DI)没有任何的了解,你可以看看我前面的笔记或者在网上搜索相关的资料进行了解. 下面通过一个实例来介绍Ninject的使用,首先我们需要猛击这里下载相关的DLL.我们仍然用到的前面的Product,实现技术所有Product的总价值.下面通过几个步

《Pro ASP.NET MVC 3 Framework》学习笔记之十八【URL和Routing】

整个项目范围的依赖注入(Project-Wide Dependency Injection) 在书接下来的章节里面,我们会看到MVC框架提供的很多不同的方式来让我们扩展和自定义对请求的处理,每一种方式都会用一个实现的接口或一个派生的基类来定义. 在第一部分的SportsStore项目实例里面已经有过引入.我们从DefaultControllerFactory类派生了一个NinjectControllerFactory类,以至于我们能够创建Controller,并使用Ninject来管理DI(依赖