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