在codeproject看到一篇文章,羣裏的一個朋友要幫忙我翻譯一下順便貼出來,這篇文章適合新手,也算是對MEF的一個簡單用法的介紹。設計模式 Introduction 架構 In a simple statement if I want to define an ASP.NET MVC controller then I can say that classes that are responsible for receiving and processing incoming http requests, handling client input, and sending response back to the client. Controllers also act as a coordinator between Model (Business) and View (Presentation). ASP.NET MVC framework itself creates controller objects at run time. There is only one prerequisite, that is controller class must have a parameter less constructor. But if you need to pass some objects with constructor then what will happen? Simply framework will fail to create controller object. In that case we need to create controller objects by our self and inject dependency there.app There are many ways you can inject dependency to a class. For example, it might be Property setter,Method, Constructor injection. In this article I will explain how to inject controller dependency to ASP.NET MVC framework with the help of constructor. Without creating custom controller factory inject dependency to controllers are not possible. So I will also explain how to create a very simple custom controller factory and register it to ASP.NET MVC framework. I will also demonstrate a way to inject dependency to controllers using Managed Extensible Framework (MEF). 框架 介紹若是我想用一個簡單的聲明去定義一個ASP.NET MVC 控制器。而後我會說這個類是負責接收和處理傳入的http請求,操做客戶端輸入,並將響應發送回客戶端。Controllers 還能夠扮演Model(業務層)和View(表現層)之間的中間人。ASP.NET MVC框架自己在運行時會建立控制器對象。可是有一個前提,那就是控制器類至少必須有一個參數的構造函數。可是,若是你須要傳遞一些對象給構造函數,那麼會發生什麼呢?簡單的框架,將沒法建立控制器對象。在這種狀況下,咱們須要建立控制器實現自我依賴注入。less 有不少方法,你能夠注入依賴一類。例如,它多是物業二傳手,方法, 構造函數注入。在這篇文章中,我將解釋如何注入控制器的依賴ASP.NET MVC框架的構造函數的幫助。沒有建立自定義控制器工廠注入依賴控制器是不可能的。因此,我也將解釋如何建立一個很是簡單的自定義控制器工廠,並把它註冊到ASP.NET MVC框架。我還將展現一種方式來注入依賴使用託管擴展框架(MEF)的控制器。 ide
這裏有不少方法能夠向類中進行依賴注入。例如,它多是屬性setter方法、構造函數注射。在本文中我將解釋向ASP.NETMVC的控制器的使用構造函數輔助進行依賴注入。淨MVC框架構造函數的幫助。沒有建立自定義控制器工廠注入依賴控制器是不可能的。因此我也會演示如何建立一個很是簡單的自定義控制器廠,並把它註冊到ASP.NET MVC框架, 我還將演示一種使用託管擴展的框架(MEF)的依賴注入控制器方式。函數 Why Need to Inject Controller Dependency?In real life application development, you will see almost all ASP.NET MVC applications are needed to inject its dependent component. You can create component directly inside the controller instead of inject them. In that case the controller will be strongly coupled on those components. If any component's implementation is changed or new version of that component is released then you must change controller class itself. Another problem you will face when you will do unit test. You cannot do unit test of those controllers independently (within isolation). You cannot take mocking features from unit testing framework. Without mocking, you cannot do unit test of your code in isolated environment. post 在現實生活中的應用程序開發,你會看到,幾乎全部的ASP.NET MVC應用程序都須要注入其依賴的組件。您能夠在控制器內部創直接建組件對象代替注入。在這種狀況下,控制器將被強耦合到這些組件。若是任何組件的實現變動或新版本組件被髮布,那麼,你必須修改控制器類。當你要作單元測試時,您將面臨的另外一個問題。 你不能單獨對這些控制器類作單元測試。你不能經過單元測試框架實現功能的模擬。沒有模擬,你就不能在隔離的環境中作單元測試的代碼。單元測試 Controller Static StructureASP.NET MVC framework’s controller structure is defined inside an abstract class named Controller. If you want to create any controller, first you will create a class which must be inherited from abstract class 控制器靜態結構ASP.NET MVC框架的控制器結構被定義在名爲Controller的抽象類。若是你想建立任何控制器,首先您必須必須繼承自抽象的Controller類 。Controller類的層次結構的UML類圖以下: All controllers root interface is 全部控制器的根接口 Simple Custom Controller Definition
If you create an ASP.NET MVC project, you will get two default controllers. One is 簡單的自定義控制器定義若是你建立一個ASP.NET MVC項目時,你會獲得兩個默認控制器。一個是 If you look at the 若是你查看HomeController中類定義,那麼你會發現,這個類沒有構造函數。
public class HomeController : Controller { public ActionResult Index() { ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application."; return View(); } public ActionResult About() { ViewBag.Message = "Your app description page."; return View(); } }
We all know that if there is no constructor defined then at compile time .NET Framework creates a default parameter-less constructor.
咱們都知道,若是沒有構造函數的定義,那麼在編譯的時候,NET Framework會建立一個默認的無參數的構造函數。
public class HomeController : Controller { public HomeController() { } } Now I will create ILogger interface and its implementation DefaultLogger class. Home controller class will use that ILogger object. I will inject it throw constructor. 如今我將建立一個ILogger接口及其實現類defaultlogger。HomeController類將使用ILogger對象。我會把它注入構造函數。 public interface ILogger { void Log(string logData); } public class DefaultLogger : ILogger { public void Log(string logData) { System.Diagnostics.Debug.WriteLine(logData, "default"); } } HomeController with ILogger constructor injection looks: HomeController的用的ILogger注入構造函數以下: public class HomeController : Controller { private readonly ILogger _logger; public HomeController(ILogger logger) { _logger = logger; } }
Still I do not find any place where I can create
我仍然沒有找到任何地方,在那裏我能夠在個人代碼庫建立DefaultLogger對象,我不是本身建立 HomeController的對象,因此我找不到
建立 DefaultLogger對象
的地方以及如何傳遞
HomeController
參數給
HomeController(
ILogger logger
)構造函數。若是我生成這個項目,那麼,它確定不會出錯。但在運行時將引起異常。 異常詳細信息的錯誤頁面將會顯示以下:
See the stack trace above, object of class
DefaultContollerActivator
throw exception named
MissingMethodException
. If you go MSDN, search the exception which is raised, you will find it there and there clearly mentions
「The exception that is thrown when there is an attempt to dynamically access a method that does not exist.」
That is the inner exception message. If see next exception named
InvalidOperationException
, it is actually wrapped
MissingMethodException
and there you will find more user friendly message and that is
「Make sure that the controller has a parameterless public constructor.」
If I want to make
HomeController
workable then I must add a parameter less constructor and framework will create controller object with the help of that constructor. Question will rise how I can pass
DefaultLogger
object to that controller? Please keep your patience.
查看堆棧跟蹤以上,DefaultContollerActivator
類
的
對象
拋出
名爲MissingMethodException異常 。若是你去MSDN,搜索是誰引起的異常,你會發現它有明確的指出「有一個試圖動態訪問的方法時不存在
引起的異常
。」這是內部異常消息。若是看到一個異常名爲 InvalidOperationException異常,它其實是包含MissingMethodException,而且你能夠找到更多的友好的提示消息,那就是「確保該控制器具備一個無參數的公共構造函數。」若是我要讓 HomeController的可行的話,我必須添加一個無參數的
構造函數
在框架的幫助下
建立控制器對象
。問題是,我怎麼向控制器傳入
DefaultLogger對象
?請保持您的耐心。
How MVC Framework Create Controllers?Before start to describe dependency injection process of MVC框架如何建立控制器?開始前描述HomeController的 If you look at the bellow image, you can see that
若是你看看波紋圖像,你能夠看到, Why Need Custom Controller Factory?Already you know that Default controller factory creates controller object using parameter less constructor. We can inject our controller by parameterless constructor too. See the code below: 爲何須要自定義控制器工廠?你已經知道,默認控制器工廠建立控制器對象使用無參構造函數。咱們可的含參的控制器構造函數實現注入。見下面的代碼: public class HomeController : Controller { private readonly ILogger _logger; public HomeController():this(new DefaultLogger()) { } public HomeController(ILogger logger) { _logger = logger; } } I found many developers who are misguided the way of the above dependency injection process. This is not dependency injection at all. This actually clearly violate the dependency inversion principle. The principle say that "High level module should not depend upon the low level module, both should depend on abstraction. Details should depends upon abstraction". In above code HomeController itself create DefaultLogger object. So it directly depends on ILogger implementation (DefaultLogger). If in future another implementation comes of ILogger interface then HomeController code itself need to change. So it is probed that it is not proper way to do. So if we need proper way to inject dependency. We need parameterised constructor, by which we can inject our ILogger component. So current implementation of DefaultControllerFactory does not support this requirement. So we need a new custom controller factory.
我發現許多開發者的錯誤就是上述依賴注入過程的方式。這根本不是依賴注入。實際上,這顯然違反了依賴倒置原則。原則上說,「 高級別不該依賴於低級別的模塊模塊,都應該依賴於抽象。細節上都要取決於抽象「。在上面的代碼中的HomeController自己建立DefaultLogger對象。所以,它直接取決於ILogger實現(DefaultLogger)。若是在將來有 ILogger接口
的另外一種實現
,那麼HomeController的代碼自己須要改變。因此
這種嘗試是不正確的方式
。所以,若是咱們須要適當的方式進行注入依賴。咱們須要含參構造函數,咱們能夠注入ILogger組件。所以,當前執行的
DefaultControllerFactory
不支持這一要求。所以,咱們須要一個新的自定義控制器工廠。
建立自定義控制器廠經過實現 public class CustomControllerFactory : IControllerFactory { public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) { ILogger logger = new DefaultLogger(); var controller = new HomeController(logger); return controller; } public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior( System.Web.Routing.RequestContext requestContext, string controllerName) { return SessionStateBehavior.Default; } public void ReleaseController(IController controller) { IDisposable disposable = controller as IDisposable; if (disposable != null) disposable.Dispose(); } }
Now at first you need to register 如今首先你須要在Application_Start方法中向MVC框架註冊CustomControllerFactory。 public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { RegisterCustomControllerFactory (); } } private void RegisterCustomControllerFactory () { IControllerFactory factory = new CustomControllerFactory(); ControllerBuilder.Current.SetControllerFactory(factory); }
If you run the application and see that your parameter-less constructor
若是你運行應用程序會看到無參的HomeController構造函數並沒有被調用,含參的
MVC
框架
調用
。哇!你的問題很容易就解決了。
You can create your controller creation with little generic way using reflection.
您可使用反射建立控制器泛型方法
public
class
CustomControllerFactory : IControllerFactory
{ private readonly string _controllerNamespace; public CustomControllerFactory(string controllerNamespace) { _controllerNamespace = controllerNamespace; } public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) { ILogger logger = new DefaultLogger(); Type controllerType = Type.GetType(string.Concat(_controllerNamespace, ".", controllerName, "Controller")); IController controller = Activator.CreateInstance(controllerType, new[] { logger }) as Controller; return controller; } } first you need to create controller type object from controller full name. Then you create controller object at run time using
首先,您須要從控制器的全名建立控制器類型的對象。而後在運行時使用
Another Approach
Another way you can create your own controller factory. Though MVC framework’s default implementation of 另外一種方法
另外一種方法,你能夠建立本身的控制器工廠。雖然MVC框架的默認實現 public class CustomControllerFactory : DefaultControllerFactory { protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType) { ILogger logger = new DefaultLogger(); IController controller = Activator.CreateInstance(controllerType, new[] { logger }) as Controller; return controller; } } Just create my own
MEF for Creating Custom Controller FactoryIn real life project you will see experts are using IOC containers inside controller factory for creating/fetching the MEF建立自定義控制器工廠在現實生活中,您將看到專業的項目在控制器工廠內部使用的是Ioc容器建立/讀取Controller對象的。若是您嘗試動態建立依賴對象和控制器對象會引起不少 問題。所以,這將是更好的方法在您的項目使用任意的IOC容器到並註冊全部的依賴對象。您可使用各類流行的IOC容器,如Castle Windsor, Unity, NInject, StructureMap etc.等,在這種狀況下我將演示如何使用託管擴展框架(MEF)。MEF並非一個IOC容器。它是一種層組合。你也能夠叫它可插拔的框架,在運行時,你能夠插入你的依賴組件。幾乎全部IOC的工做均可以由MEF完成。採用MEF仍是IOC這取決於你應用架構的要求。個人建議是,當你須要在運行時修改你的組件時(運行時間插件在運行),在這種狀況下,您可使用MEF,當您建立組件以及依賴於它的靜態引用的組件,那麼你可使用IOC。有不少網上公佈文章,哪裏能夠找到有關的更多詳細信息/技術文章。我但願你能多學習,那麼當你使用時很容易地做出決定。順便說一下MEF來自.NET 4.0框架,所以,主要的好處是,不存在第三方組件的依賴。首先,你須要在你的您的項目引用system.ComponentModel.Composition。 Then you create custom Controller Factory. The name of the controller factory is 接下來建立自定義控制器工廠----- public class MefControllerFactory : DefaultControllerFactory { private readonly CompositionContainer _container; public MefControllerFactory(CompositionContainer container) { _container = container; } protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType) { Lazy<object, object> export = _container.GetExports(controllerType, null, null).FirstOrDefault(); return null == export ? base.GetControllerInstance(requestContext, controllerType) : (IController)export.Value; } public override void ReleaseController(IController controller) { ((IDisposable)controller).Dispose(); } }
這裏的CompositContainer對象是很像IOC容器。內部GetControllerInstance方法用於獲取取控制器對象。若是發現空值,那麼返回默認的控制器(基類)的對象,不然返回 CompositContainer對象。在建立MefControllerFactory 類,咱們須要在在Application Start事件將其註冊到MVC 框架。 protected void Application_Start() { var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); var composition = new CompositionContainer(catalog); IControllerFactory mefControllerFactory = new MefControllerFactory(composition); ControllerBuilder.Current.SetControllerFactory(mefControllerFactory); } I use
MEF的
InheritedExportAttribute
ExportAttribute,
PartCreationPolicyAttribute 到接口
ILogger
,
HomeController
Based on these attributes MEF framework create objects. Just one important think you should remember that when you will use MEF, you should decorate your controllers through
基於這些屬性MEF框架建立對象。只是一個重要的知識點你應該記住,當你將使用MEF,你應該裝飾你的控制器經過PartCreationPolicyAttribute 併爲控制器建立的每一個請求策略的對象設置生命週期 [PartCreationPolicy(CreationPolicy.NonShared)], 若是沒有就會報錯。默認狀況下,它將使用SharedCreation策略。
Points of InterestI tried to explain various ways to create controller factory and inject its dependency in a very simple and straight forward way. First and second approach is just make understandable to the process of building and injecting controller and its dependency. You should not use that approach directly to the real life application. You can take IOC or MEF framework for that. You can do more research on MEF and learn MEF usage and best practices after that you will use it to your real life projects. Near future I have a plan to write another article that will demonstrate to use of various IOC containers like Windsor, Unity, NInject, StrucutureMap etc. to inject controller dependency injection and a comparative study with them. In my sample downloadable code I used visual studio 2012 with .NET framework 4.5. Anyone can download and play with that. 亮點我試圖解釋各類不一樣的方法來建立一個很是簡單直觀的注入依賴的控制器的工廠方法。這兩種方法是要理解構造和控制器的依賴注入過程。你不該該將其直接應用與真實的項目中。你可使用IOC或MEF框架。你能夠作更多的研究和學習使用MEF及最佳作法以後,能夠將它應用到你的現實生活中的項目。不久,我計劃寫一篇文章,將展現使用多種IoC容器像Windsor, Unity, NInject, StrucutureMap etc.等注入控制器依賴注入並進行比較和研究。 在個人示例下載的代碼,我用的是Visual Studio 2012 .NET Framework 4.5。任何人均可如下載玩一玩。 |