MVC框架之因此如此受欢迎的缘由之一就是它十分注意支持关注分离,使各个功能部件尽可能可以相互独立。今天咱们就来看看MVC4如何使用DI方法实现一些组件的独立,使原本结合紧密的部件,松耦合。我如今所说的对于.net的一些初学者来讲可能有点拗口,其实我也是一个实打实的初学者,本身开始看这段话的时候迟迟不能理解,可是当看了实例以后,消化了一下就还算是懂得了其中的一些韵味了。下面就让我来和你们分享一下我本身所理解的依赖性注入。但愿你们能多指教。浏览器
那么接下来咱们来看一个简单的例子,用Demo说话mvc
咱们新建一个MVC4的项目吧框架
而后选择Basic模板函数
点击Ok建立好项目this
接着在Models文件夹添加一个IEmailSender接口,代码以下spa
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DIShow.Models { public interface IEmailSender { public string SendEmail(); } }
接着再添加一个EmailSender类实现IEmailSender接口,代码以下.net
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace DIShow.Models { public class EmailSender:IEmailSender { public string SendEmail() { return "My Name is SendEmail,My Type is EmailSender"; } } }
如今咱们在Controller文件夹里添加一个HomeControllercode
咱们要实现的功能就是在Controller里调用SendEmail方法来发送一个邮件。对象
咱们在controller里添加以下代码就能够了。blog
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using DIShow.Models; namespace DIShow.Controllers { public class HomeController : Controller { private IEmailSender emailSender; public string Index() { emailSender = new EmailSender(); return emailSender.SendEmail(); } } }
这个时候咱们运行程序,就能够看到浏览器里的输出
如今咱们惟一能够肯定的就是这个程序时正确的。
那么如今咱们就来谈谈这样简单的一个程序能够怎样作,来让他更为合理,具备清晰的结构。
首先咱们能够看到,咱们有一个接口和一个实现这个接口的类,确定有同窗会想问,就这样的程序干吗还要画蛇添足搞个接口,直接在controller里面实例化这个类,再调用SendEmail方法就行了。我想说的是,接口只是为了后面的改进作一个铺垫,如今看来确实是无关紧要。
而后如今我提出一个问题,要是我有多个发送邮件的程序,也就是说有多个相似于EmailSender这样的类。要是我想换一个发送程序,岂不是我每次必需要修改控制器中的代码,以此来切换发送程序。这样的作法对于很小的程序来讲还好,对于稍微大一点的程序就会变得很不合理。这样就把控制器的代码变得十分繁琐了。并且对于MVC程序来讲控制器就至关于大脑,你不能老是修改大脑,最合理的方式就是修改提供程序,而后大脑只须要一个调用执行该方法的接口就好了,并不须要关心具体是怎么实现的。回到咱们如今的例子,咱们要实现的效果就是在controller里面不出现EmailSender,只须要一个IEmailSender接口。咱们只须要实例化这个接口的具体实现就好了。因此咱们能够对控制器中的代码进行以下改进
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using DIShow.Models; namespace DIShow.Controllers { public class HomeController : Controller { private IEmailSender emailSender; public HomeController(IEmailSender emailSender) { this.emailSender = emailSender; } public string Index() { return emailSender.SendEmail(); } } }
咱们能够看到这时咱们就实现了HomeController和EmailSender之间毫无联系,固然这样的程序时没办法运行的,由于程序并不知道如何实例化IEmailsender这个接口,虽然有一个实现了这个接口的类,可是咱们并无告诉程序应该用哪一个类去实例化这个接口。因此接下来咱们就要去告诉程序应该用哪一个类去实例化这个接口。
解决问题的方法就是“DI容器”,这个容器就是在接口(例如IEmailSender)和实现接口的具体类(例如EmailSender)之间担任一个中间人,由他来处理具体经过实例化哪一个类来实例化接口。
而DI容器应该如何实现呢,两种方法,第一种就是本身建立一个DI容器,第二种就是用网上的开源代码,本人用的是Ninject包,网址:http://www.ninject.org
而在本篇博客中,因为做者本人能力有限,因此将只演示第二种方法。
你们能够在我提供的网址上看一下该包的具体细节而后下载包来进行使用,也能够直接在VS中的引用中进行安装。以下图
安装好后接下来咱们就开始用使用这个包来帮助咱们建立DI容器
第一步:建立一个依赖性链解析器
这个解析器就是相似搞出一个中介,让程序知道哪个类去实例化哪个接口。咱们能够在mvc项目中,新建一个文件夹,例如Infrastructure,而后在里面建一个类:NinjectDependencyResolver 并实现IDependencyResolver接口,代码以下
using System; using System.Collections.Generic; using System.Linq; using System.Web; using Ninject; using System.Web.Mvc; using DIShow.Models; namespace DIShow.Infrastructure { public class NinjectDependenceyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependenceyResolver() { kernel = new StandardKernel(); AddBindings(); } public object GetService(Type serviceType)//IDependencyResolver的方法 { return kernel.TryGet(serviceType); } public IEnumerable<object> GetServices(Type serviceType)//IDependencyResolver的方法 { return kernel.GetAll(serviceType); } private void AddBindings() { kernel.Bind<IEmailSender>().To<EmailSender>(); } } }
第二步:注册依赖解析器
经过注册依赖解析器来告诉MVC框架,用户但愿使用本身的依赖解析器,那么在哪里注册呢,固然是在管理整个程序运行的地方注册-Global.asax.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing; using DIShow.Infrastructure; namespace DIShow { // Note: For instructions on enabling IIS6 or IIS7 classic mode, // visit http://go.microsoft.com/?LinkId=9394801 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); DependencyResolver.SetResolver(new NinjectDependenceyResolver()); } } }
DependencyResolver.SetResolver(new NinjectDependenceyResolver())便实现了注册
经过以上两步DI容器的建立和注册就已经搞定了。
如今咱们的这个小项目的依赖性注入就彻底搞定了,咱们来运行程序检测一下正确与否
事实说明是正确的。
最后咱们仍是来简单总结一下吧。
首先咱们来梳理一下程序的运行过程:程序启动,根据路由系统咱们到了HomeController,而后运行到HomeController构造函数的时候发现须要传入一个IEmailSender的实例化对象。这个时候程序回到Global当中,发现Global确实注册这样一件事,就是咱们指定了怎样去实例化接口。经过咱们的注册信息,咱们找到了咱们的DI容器,也就是NinjectDependenceyResolver类。而后咱们在这个类里面传入一个类型给GetService,而后它经过查看咱们的绑定信息,这个中介就发现咱们是把IEmailSender绑定到EmailSender上面去的。因而就 实例化了EmailSender获得了一个对象,最后返回给了在HomeController中的构造函数中的参数sendEmail。因而后面就能够成功执行方法了。
而后咱们来看一下实现这些过程主要作了那几步:
1.写出接口和实现类。
2.在控制器中只调用接口方法,不出现具体类
3.建立一个DI容器,将接口和具体类绑定
4.在Global中注册这个容器
就这样四步就搞定了。之后要是想要切换另外一个发送程序,只须要在DI容器中将接口绑定到另外一个实际类上就能够了,控制器不须要作任何修改。
好啦,个人分享就到此为止了,之后要是学到了更多有意思的东西还会和你们继续分享的。