原文连接:http://www.codeproject.com/Articles/560798/ASP-NET-MVC-Controller-Dependency-Injection-for-Beapp
前言:在这篇文章中,我将经过一个demo,直截了当地说明依赖注入在MVC框架中的使用。框架
内容列表:ide
1.介绍函数
2.为何使用控制器依赖注入单元测试
3.控制器静态结构测试
4.自定义控制器ui
5.Framework中控制器的建立this
6.为何使用控制器工厂模式spa
7.控制器工厂模式线程
7.1.目标1
7.2.目标2
8.使用MEF实现控制器工厂模式
9.重点补充
一:介绍
首先简单地说明控制器在MVC框架中要作的几件事情
1.接收HTTP请求
2.处理HTTP请求
3.操做客户端输入的数据
4.发送回复给客户端
5.做为Model和View的中转站
MVC框架在运行时本身建立控制器对象,有一个先决条件,控制器类的构造函数是无参的。若是你想传递一个对象做为控制器的参数,这种状况咱们又该如何处理?建立这种类型的控制器会失败,咱们须要建立本身的控制器,将控制器参数注入到控制器。
有多种方式能够将参数注入到控制器的构造方法中
1.设置属性
2.经过方法
3.构造方法
在这篇文章中,我将解释如何使用控制器注入到MVC中的构造函数中。若是不使用自定义控制器工厂模式,控制器注入是没法实现的。固然我也会解释如何建立简单的控制器工厂,而后注册到MVC框架。我也会展现一种方法注入控制器,使用MEF。
二.为何使用控制器注入
在现实的程序开发中,你会看到绝大多数的MVC程序须要注入它所依赖的组件。你能够直接建立组件在控制器中,而不须要注入它们。在这种状况下,组件和控制器紧密结合,若是一个组件的扩展发生了改变,或者一个新版本的组件要使用,你就须要改变控制器中的实现(PS:讲解为何使用控制器注入)
当你想使用单元测试的时候,另外一种困难你可能会遇到。你不能测试这些控制器在一个独立的单元。你不能模仿一些新的特性,若是不能模仿,你将不能成功运行你的代码在一个独立的环境。
三.控制器静态结构
MVC框架中的控制器结构是定义在一个叫Controller的抽象类中,若是你想建立一些控制器,首先你须要建立一个类,从抽象类Controller中继承,UML类图以下:
全部的控制器都有一个根接口IController,抽象类ControllerBase从它去实现本身的方法。另外一个抽象类从ControllerBase中继承,这个类就是Controller,全部的自定义的控制器类都要从Controller中继承,或者从它的子类中继承。
四.简单的自定义控制器
若是你建立一个MVC工程,你将会获得两个控制器,AccountController和HomeController
若是你去看HomeController中的代码实现,你会发现它没有本身的构造方法。
1 public class HomeController : Controller 2 { 3 public ActionResult Index() 4 { 5 ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application."; 6 return View(); 7 } 8 public ActionResult About() 9 { 10 ViewBag.Message = "Your app description page."; 11 return View(); 12 } 13 }
咱们都知道这里没有一个构造方法,但在编译时会建立一个无参的构造方法。
1 ublic class HomeController : Controller 2 { 3 public HomeController() 4 { 5 } 6 }
如今我将要建立一个ILogger接口及它的一个实现类DefaultLogger类,Home控制器会使用ILogger类型的对象做为参数,注入到它的控制器构造方法中。
1 public interface ILogger 2 { 3 void Log(string logData); 4 } 5 public class DefaultLogger : ILogger 6 { 7 public void Log(string logData) 8 { 9 System.Diagnostics.Debug.WriteLine(logData, "default"); 10 } 11 }
带参ILogger的Home控制器构造方法以下:
1 public class HomeController : Controller 2 { 3 private readonly ILogger _logger; 4 public HomeController(ILogger logger) 5 { 6 _logger = logger; 7 } 8 }
直到如今你也没找到咱们在什么地方实例化了DefaultLogger对象,也不知道如何传递这个对象到控制器的构造方法中,在编写代码阶段程序不会报错,可是在运行代码会报错,以下:
看上面的线程记录,DefaultControllerActivator对象会抛出一个异常MissingMethonException。若是你到MSDN,找这个异常是如何引起的,你会发现找到不到适当的方法。看接下来的异常InvalidOperationException,它确实包含了MissingMethodException,将下来你会看到更加有用的信息,确保在控制器构造方法中有一个带参数的构造方法。若是想让代码工做正常,我必须要加带一个参数的构造方法,框架会建立控制器对象经过咱们建立的那个构造方法。问题在于,咱们如何传递一个一个DefaultLogger对象到这个控制器。请继续你好练习,咱们接着往下看。
五.MVC框架是如何建立控制器对象
在咱们开始注入DefaultLogger对象到HomeController以前,咱们要有一个概念,MVC框架是如何建立一个控制器对象的。IControllerFactory接口的主要责任就是建立控制器对象。DefaultControllerFactory是框架默认提供的可扩展的类。若是你添加一个无参的构造方法,而后设置一个断点,你将会发现程序在这一刻,将停留在这里。
看上面的图片,你能够看到IControllerFactory类型的一个DefaultControllerFactory对象,DefaultControllerFactory有一些方法,如:Create,GetControllerInstance,CreateController,这些方法将会建立一个HomeController对象,MVC框架是开源的,若是你想知道更多的方法,能够下载官方的资源,本身去阅读。你看调试的代码,你能够看到DefaultControllerFactory对象被CurrentControllerFactory
六.为何要自定义控制器工厂
如今咱们知道默认的控制器工厂使用一个无参的构造方法建立一个控制器对象。咱们能够注入本身的带参的控制器构造方法。
1 public class HomeController : Controller 2 { 3 private readonly ILogger _logger; 4 public HomeController():this(new DefaultLogger()) 5 { 6 } 7 public HomeController(ILogger logger) 8 { 9 _logger = logger; 10 } 11 }
我发现许多的开发者都对上面依赖注入有所误解,它不是一个依赖注入的形式。这个确实违反了组件的原则。而这个原则的愿意是:上层的模块不能依赖于低层级的模块,双方都要依赖于抽象层,细节要在体如今抽象层。在上面的代码中,HomeController建立了本身的DefaultLogger对象。它直接依赖于ILogger接口的扩展(DefaultLogger),若是在未来一个新的扩展(扩展了ILogger接口),咱们须要修改咱们HomeController中的方法,因此咱们须要使用适当的方法去注入咱们的组件。咱们要使用一个带参的构造方法去注入咱们的ILogger 组件,可是默认的 DefaultControllerFactory不支持咱们这么作,因此咱们要建立本身的控制器工厂。
七.自定义控制器工厂
我使用两种方法展现如何建立本身的控制器工厂。
7.1途径1
咱们能够建立一个新的控制器工厂,扩展了IControllerFactory接口,假定咱们自定义的控制器工厂的名称叫CustomControllerFactory,以下
1 public class CustomControllerFactory : IControllerFactory 2 { 3 public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) 4 { 5 ILogger logger = new DefaultLogger(); 6 var controller = new HomeController(logger); 7 return controller; 8 } 9 public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior( 10 System.Web.Routing.RequestContext requestContext, string controllerName) 11 { 12 return SessionStateBehavior.Default; 13 } 14 public void ReleaseController(IController controller) 15 { 16 IDisposable disposable = controller as IDisposable; 17 if (disposable != null) 18 disposable.Dispose(); 19 } 20 }
如今第一步,咱们须要将CustomControllerFactory注册到MVC框架,完成这件事要在Application_Start事件中书写代码。
1 public class MvcApplication : System.Web.HttpApplication 2 { 3 protected void Application_Start() 4 { 5 RegisterCustomControllerFactory (); 6 } 7 } 8 private void RegisterCustomControllerFactory () 9 { 10 IControllerFactory factory = new CustomControllerFactory(); 11 ControllerBuilder.Current.SetControllerFactory(factory); 12 }
若是你运行你的程序,你会发现那个无参的构造方法没有被执行,那个带参的构造方法执行了。你的问题就这么简单的解决了。
你能够构建你的控制器工厂使用反射机制。
1 public class CustomControllerFactory : IControllerFactory 2 { 3 private readonly string _controllerNamespace; 4 public CustomControllerFactory(string controllerNamespace) 5 { 6 _controllerNamespace = controllerNamespace; 7 } 8 public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) 9 { 10 ILogger logger = new DefaultLogger(); 11 Type controllerType = Type.GetType(string.Concat(_controllerNamespace, ".", controllerName, "Controller")); 12 IController controller = Activator.CreateInstance(controllerType, new[] { logger }) as Controller; 13 return controller; 14 } 15 }
7.2途径2
这里方法不是去扩展IControllerFactory接口,而是去继承DefaultControllerFactory类,经过修改其中的方法。固然也要将控制器工厂注入到程序启动的事件中。代码以下:
1 public class CustomControllerFactory : DefaultControllerFactory 2 { 3 protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType) 4 { 5 ILogger logger = new DefaultLogger(); 6 IController controller = Activator.CreateInstance(controllerType, new[] { logger }) as Controller; 7 return controller; 8 } 9 }
(去掉了MEF建立自定义控制器工厂的方法,由于本身也实在不能理解,可是要看啊)。