解释:
依赖注入:(Dependency Injection),也称之为控制反转。
大致意思就是,让我们的应用程序所依赖的一些外部服务,可以根据需要动态注入,而不是预先在应用程序中明确的约束.这种思想在当前软件开发领域,为了保证软件架构灵活性和可便于维护性,应该说是很有意义的。在MVC框架中,为依赖注入的设计提供了先天的支持。结合一些我们熟知的DI组建,比如Unity,Ninject等等。可以较为容易的实现上述提到的功能。
场景介绍:
我们的应用程序,需要支持各种不同的数据源,而且我们希望日后可以很容易地切换,不会因为数据源的变化而导致对Contoller或者Model,或者View做修改。
第一步:准备一个MVC项目(选择空模板)
第二步:准备一个实体模型
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SportsStore.Domain.Entities { public class Employee { public int ID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } }
第三步:定义一个数据访问接口
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SportsStore.Domain.Entities; namespace SportsStore.Domain.Abstract { public interface IEmployeeRepository { IQueryable<Employee> Employees { get; } } }
第四步:添加一个HomeController
public class HomeController : Controller { // // GET: /Home/ public ActionResult Index() { return View(); } }
注意,这里需要为HomeController添加一个特殊的构造函数,传入IEmployeeRepository这个接口。通常,所有的DI组件都是通过这样的方式注入的。
在设计HomeController的时候,我们不需要关心到底日后会用具体的哪种IEmployeeRepository,我们只是要求要传入一个IEmployeeRepository的具体实现就可以了,这就是DI的本质了。
到这里为止,我们该做的准备工作基本就绪了。下面来看看如何结合DI组件来实现我们的需求
第五步:引入NInject组件
这是我比较喜欢的一个DI组件。它还针对MVC3专门有一个扩展
第六步:创建一个IEmployeeRepository的具体实现
using System; using System.Collections.Generic; using System.Linq; using System.Text; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; namespace SportsStore.Domain.Concrete { public class EmployeeRepository : IEmployeeRepository { public IQueryable<Employee> Employees { get { return new[] { new Employee { ID = 1, FirstName = "ICEY", LastName = "chen" } }.AsQueryable(); } } } }
第七步:实现注入,通过扩展MVC组件实现
第一种:实现自定义ControllerFactory
我们都知道,Controller其实都是由ControllerFactory来生成的,那么,为了给所有新创建从Controller都自动注入我们的服务,那么就可以从ControllerFactory这个地方动动脑筋了。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using Ninject; using Moq; using SportsStore.Domain.Abstract; using SportsStore.Domain.Entities; using SportsStore.Domain.Concrete; using System.Configuration; using SportsStore.WebUI.Infrastructure.Abstract; using SportsStore.WebUI.Infrastructure.Concrete; namespace SportsStore.WebUI.Infrastructure { /// <summary> /// 使用Ninject生成mvc应用程序controller并处理DI /// </summary> public class NinjectControllerFactory:DefaultControllerFactory { private IKernel ninjectKernel; public NinjectControllerFactory() { ninjectKernel = new StandardKernel(); AddBindings(); } protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType) { return controllerType == null ? null : (IController)ninjectKernel.Get(controllerType); } private void AddBindings() { ninjectKernel.Bind<IEmployeeRepository>().To<EmployeeRepository>(); } } }
要使用这个自定义的 ControllerFactory,我们需要修改Global.ascx文件中的Application_Start方法,添加下面的代码
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
这样做好之后,我们可以测试HomeController中的Index这个Action,我们发现它还是能正常工作。
修改HomeController中的Index
public class HomeController : Controller { private IEmployeeRepository repository; public HomeController(IEmployeeRepository employeeRepository) { repository = employeeRepository; } public ActionResult Index() { return View(repository.Employees); } }
运行程序,我们发现程序能很好的工作了。
第二种:实现自定义的DependencyResolver
顾名思义,这就是MVC框架里面专门来处理所谓的依赖项的处理器。可以说这是MVC专门为DI准备的一个后门。下面是我写好的一个例子
public class NinjectDependencyResolver : IDependencyResolver { private IKernel kernel; public NinjectDependencyResolver() { kernel = new StandardKernel(); AddBindings(); } public object GetService(Type serviceType) { return kernel.TryGet(serviceType); } public IEnumerable<object> GetServices(Type serviceType) { return kernel.GetAll(serviceType); } public IBindingToSyntax<T> Bind<T>() { return kernel.Bind<T>(); } public IKernel Kernel { get { return kernel; } } private void AddBindings() { Bind<IEmployeeRepository>().To<EmployeeRepository>(); } }
那么,如何使用这个自定义的处理器呢?
很简单,我们仍然是修改Global.asax文件中的Application_Start方法 DependencyResolver.SetResolver(new NinjectDependencyResolver());
请注意,之前那个设置ControllerFactory的代码,我们可以注释掉了
至此,Ninject注入容器完全融入MVC的心脏了。