Agenda 
前言 前篇介紹MVC使用HttpHandler是MvcHandler透過並MvcRouteHandler物件來返回.
我有做一個可以針對於Asp.net MVC Debugger 的專案,只要下中斷點就可輕易進入Asp.net MVC原始碼.
大家介紹如何取得Controller執行物件
取得執行Controller 在ProcessRequest方法是透過ProcessRequestInit取得執行controller物件,讓我們看看是這個方法如何controller物件.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 private  void  ProcessRequestInit (HttpContextBase httpContext, out  IController controller, out  IControllerFactory factory ){     HttpContext currentContext = HttpContext.Current;     if  (currentContext != null )     {         bool ? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(currentContext);         if  (isRequestValidationEnabled == true )         {             ValidationUtility.EnableDynamicValidation(currentContext);         }     }     AddVersionHeader(httpContext);     RemoveOptionalRoutingParameters();          string  controllerName = RequestContext.RouteData.GetRequiredString("controller" );          factory = ControllerBuilder.GetControllerFactory();     controller = factory.CreateController(RequestContext, controllerName);     if  (controller == null )     {         throw  new  InvalidOperationException(             String.Format(                 CultureInfo.CurrentCulture,                 MvcResources.ControllerBuilder_FactoryReturnedNull,                 factory.GetType(),                 controllerName));     } } 
從上面程式碼可以得知我們執行Controller物件實現於IController介面,並會呼叫IController.Execute方法.
IController介面是同步的方式執行。為了支持非同步請求處理,IController介面非同步版本System.Web.Mvc.IAsyncController被定義出来。IAsyncController介面通過BeginExecute/EndExecute方法组合来完成。
 
1 2 3 4 5 6 7 8 9 10 public  interface  IController {     void  Execute (RequestContext requestContext ) } public  interface  IAsyncController  : IController {     IAsyncResult BeginExecute (RequestContext requestContext, AsyncCallback callback, object  state ) ;     void  EndExecute (IAsyncResult asyncResult ) } 
透過RouteData.GetRequiredString取得執行Controller名稱,經由RouteValueDictionary查找之前註冊Url樣板並解析此次要使用Controller名稱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public  string  GetRequiredString (string  valueName{     object  obj;     if  (this .Values.TryGetValue(valueName, out  obj))     {         string  str = obj as  string ;         if  (!string .IsNullOrEmpty(str))             return  str;     }     throw  new  InvalidOperationException(string .Format((IFormatProvider) CultureInfo.CurrentUICulture, System.Web.SR.GetString("RouteData_RequiredValue" ), new  object [1 ]     {     (object ) valueName     })); } 
ControllerBuilder ControllerBuilder類別定義一個Current靜態只讀屬性現在返回ControllerBuilder物件是一個全域物件。SetControllerFactory方法重載用於註冊ControllerFactory類型或物件,而GetControllerFactory方法返回一個具體ControllerFactory物件。
我們透過GetControllerFactory取得返回Controller工廠.
1 2 3 4 5 6 7 8 9 public  class  ControllerBuilder {     public  IControllerFactory GetControllerFactory ()     public  void  SetControllerFactory (Type controllerFactoryType )     public  void  SetControllerFactory (IControllerFactory controllerFactory )     IControllerFactory GetControllerFactory () ;     public  HashSet<string > DefaultNamespaces { get ; }     public  static  ControllerBuilder Current { get ; } } 
GetControllerFactory透過private IResolver<IControllerFactory>取得要執行的ControllerFactory.
一般來說沒有設置就是使用DefaultControllerFactory工廠來取得Controller物件
1 2 3 4 5 6 7 8 9 10 11 12 public  IControllerFactory GetControllerFactory (){     return  _serviceResolver.Current; } internal  ControllerBuilder (IResolver<IControllerFactory> serviceResolver ){ _serviceResolver = serviceResolver ?? new  SingleServiceResolver<IControllerFactory>(                 () => _factoryThunk(),                 new  DefaultControllerFactory { ControllerBuilder = this  },                 "ControllerBuilder.GetControllerFactory" ); } 
IControllerFactory介面 IControllerFactory介面有三個方法.
CreateController取得Controller物件(工廠模式最重要方法)GetControllerSessionBehavior取得Session
Default:使用預設ASP.NET Session狀態行為。 
Required:使用完全的讀和寫Session狀態行為。 
ReadOnly:使用只讀Session狀態。 
Disabled:不使用Session狀態。 
 
ReleaseController釋放使用資源 
1 2 3 4 5 6 public  interface  IControllerFactory {     IController CreateController (RequestContext requestContext, string  controllerName ) ;     SessionStateBehavior GetControllerSessionBehavior (RequestContext requestContext, string  controllerName ) ;     void  ReleaseController (IController controller ) } 
ControllerFactory(DefaultControllerFactory.cs) 既然知道透過哪個工廠來產生Controller我們繼續追工廠是如何產生Controller物件
GetControllerType取得要執行Controller類型GetControllerInstance取得Controller物件並返回使用 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public  virtual  IController CreateController (RequestContext requestContext, string  controllerName ){     if  (requestContext == null )     {         throw  new  ArgumentNullException("requestContext" );     }     if  (String.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())     {         throw  new  ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName" );     }     Type controllerType = GetControllerType(requestContext, controllerName);     IController controller = GetControllerInstance(requestContext, controllerType);     return  controller; } 
GetControllerInstance通過反射(系統不會對建立的Controller進行快取
使用IControllerActivator(預設DefaultControllerActivator) 來建立Controller物件
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 protected  internal  virtual  IController GetControllerInstance (RequestContext requestContext, Type controllerType ){     if  (controllerType == null )     {         throw  new  HttpException(404 ,                                 String.Format(                                     CultureInfo.CurrentCulture,                                     MvcResources.DefaultControllerFactory_NoControllerFound,                                     requestContext.HttpContext.Request.Path));     }     if  (!typeof (IController).IsAssignableFrom(controllerType))     {         throw  new  ArgumentException(             String.Format(                 CultureInfo.CurrentCulture,                 MvcResources.DefaultControllerFactory_TypeDoesNotSubclassControllerBase,                 controllerType),             "controllerType" );     }          return  ControllerActivator.Create(requestContext, controllerType); } 
建立Controller的IControllerActivator 上面說GetControllerInstance會透過一個ControllerActivator,而ControllerActivator預設其實是DefaultControllerActivator類別幫助我們建立Controller物件透過Create方法.
以下是DefaultControllerActivator程式碼
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 private  class  DefaultControllerActivator  : IControllerActivator {     private  Func<IDependencyResolver> _resolverThunk;     public  DefaultControllerActivator ()         : this (null      {     }     public  DefaultControllerActivator (IDependencyResolver resolver )     {         if  (resolver == null )         {             _resolverThunk = () => DependencyResolver.Current;         }         else          {             _resolverThunk = () => resolver;         }     }     public  IController Create (RequestContext requestContext, Type controllerType )     {         try          {             return  (IController)(_resolverThunk().GetService(controllerType) ?? Activator.CreateInstance(controllerType));         }         catch  (Exception ex)         {             throw  new  InvalidOperationException(                 String.Format(                     CultureInfo.CurrentCulture,                     MvcResources.DefaultControllerFactory_ErrorCreatingController,                     controllerType),                 ex);         }     } } 
能看到這邊依賴一個IDependencyResolver,這裡先埋個小伏筆後面幾篇會為各位解答.
DefaultControllerActivator透過Activator.CreateInstance產生Controller物件,使用無建構子參數的Create方式
 
小結: 今天我們學到如何取得Controller執行物件
透過一個IControllerFactory工廠物件取得Controller執行物件,對於外部提供可替換點. 
利用RouteData.GetRequiredString取得執行的Controller名稱 
DefaultControllerFactory透過反射方式動態建立物件. 
工廠模式主要核心把如何使用物件跟如何建立物件中間解耦合,使用方不關心如何產生物件,只專注於此物件可執行的能力(介面)
下圖是本次介紹類別UML關係圖
MvcHandler是MVC的核心類別,借由ControllerBuilder創件者來取得產生Controller的工廠(預設使用DefaultControllerFactory),並呼叫CreateController方法來產生一個Controller
__此文作者__:Daniel Shih(石頭)https://isdaniel.github.io/ithelp-day11/  CC BY-NC-SA 3.0 TW  許可協議。轉載請註明出處!