MVC依赖性注入概述

MVC框架之所以如此受欢迎的原因之一就是它十分注意支持关注分离,使各个功能部件尽量能够相互独立。今天我们就来看看MVC4如何使用DI方法实现一些组件的独立,使本来结合紧密的部件,松耦合。我现在所说的对于.net的一些初学者来说可能有点拗口,其实我也是一个实打实的初学者,自己开始看这段话的时候迟迟不能理解,但是当看了实例之后,消化了一下就还算是懂得了其中的一些韵味了。下面就让我来和大家分享一下我自己所理解的依赖性注入。希望大家能多指教。

那么接下来我们来看一个简单的例子,用Demo说话


我们新建一个MVC4的项目吧

S68ZLSD_]NY2NL{$Q_GU%_B

然后选择Basic模板

EJGEL7@~O15POM`FLPN[HGF

点击Ok创建好项目

接着在Models文件夹添加一个IEmailSender接口,代码如下

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接口,代码如下

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文件夹里添加一个HomeController

我们要实现的功能就是在Controller里调用SendEmail方法来发送一个邮件。

我们在controller里添加如下代码就可以了。

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();
        }

    }
}

这个时候我们运行程序,就可以看到浏览器里的输出

image

现在我们唯一可以确定的就是这个程序时正确的。


那么现在我们就来谈谈这样简单的一个程序可以怎样做,来让他更为合理,具有清晰的结构。

首先我们可以看到,我们有一个接口和一个实现这个接口的类,肯定有同学会想问,就这样的程序干嘛还要多此一举搞个接口,直接在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中的引用中进行安装。如下图

BU1VM}N3V5N28VO]0S]HQ34

F7[X]1N35$}}R`IORH]2EWR

安装好后接下来我们就开始用使用这个包来帮助我们创建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容器的创建和注册就已经搞定了。


现在我们的这个小项目的依赖性注入就完全搞定了,我们来运行程序检测一下正确与否

image

事实说明是正确的。


最后我们还是来简单总结一下吧。

首先我们来梳理一下程序的运行过程:程序启动,根据路由系统我们到了HomeController,然后运行到HomeController构造函数的时候发现需要传入一个IEmailSender的实例化对象。这个时候程序回到Global当中,发现Global确实注册这样一件事,就是我们指定了怎样去实例化接口。通过我们的注册信息,我们找到了我们的DI容器,也就是NinjectDependenceyResolver类。然后我们在这个类里面传入一个类型给GetService,然后它通过查看我们的绑定信息,这个中介就发现我们是把IEmailSender绑定到EmailSender上面去的。于是就 实例化了EmailSender得到了一个对象,最后返回给了在HomeController中的构造函数中的参数sendEmail。于是后面就可以成功执行方法了。

然后我们来看一下实现这些过程主要做了那几步:

1.写出接口和实现类。

2.在控制器中只调用接口方法,不出现具体类

3.创建一个DI容器,将接口和具体类绑定

4.在Global中注册这个容器

就这样四步就搞定了。以后要是想要切换另一个发送程序,只需要在DI容器中将接口绑定到另一个实际类上就可以了,控制器不需要做任何修改。

 

好啦,我的分享就到此为止了,以后要是学到了更多有意思的东西还会和大家继续分享的。

posted on 2016-04-21 21:57  PengL  阅读(812)  评论(0编辑  收藏  举报

导航