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 許可協議。轉載請註明出處!