原文連接: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建立自定義控制器工廠的方法,由於本身也實在不能理解,可是要看啊)。