前篇介紹MVC使用HttpHandler
是MvcHandler
透過並MvcRouteHandler
物件來返回.
我有做一個可以針對於Asp.net MVC Debugger的專案,只要下中斷點就可輕易進入Asp.net MVC原始碼.
大家介紹如何取得Controller
執行物件
在ProcessRequest
方法是透過ProcessRequestInit
取得執行controller
物件,讓我們看看是這個方法如何controller
物件.
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();
// Get the controller type
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
// Instantiate the controller and call Execute
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
方法组合来完成。
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
名稱
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
類別定義一個Current
靜態只讀屬性現在返回ControllerBuilder
物件是一個全域物件。SetControllerFactory
方法重載用於註冊ControllerFactory
類型或物件,而GetControllerFactory
方法返回一個具體ControllerFactory
物件。
我們透過GetControllerFactory
取得返回Controller
工廠.
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
物件
public IControllerFactory GetControllerFactory()
{
return _serviceResolver.Current;
}
internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver)
{
_serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>(
() => _factoryThunk(),
new DefaultControllerFactory { ControllerBuilder = this },
"ControllerBuilder.GetControllerFactory");
}
IControllerFactory
介面有三個方法.
CreateController
取得Controller
物件(工廠模式最重要方法)GetControllerSessionBehavior
取得Session
ASP.NET
Session狀態行為。ReleaseController
釋放使用資源public interface IControllerFactory
{
IController CreateController(RequestContext requestContext, string controllerName);
SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
void ReleaseController(IController controller);
}
既然知道透過哪個工廠來產生Controller
我們繼續追工廠是如何產生Controller
物件
GetControllerType
取得要執行Controller
類型GetControllerInstance
取得Controller
物件並返回使用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
物件
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");
}
//使用IControllerActivator(預設DefaultControllerActivator) 來建立Controller物件
return ControllerActivator.Create(requestContext, controllerType);
}
上面說GetControllerInstance
會透過一個ControllerActivator
,而ControllerActivator
預設其實是DefaultControllerActivator
類別幫助我們建立Controller
物件透過Create
方法.
以下是DefaultControllerActivator
程式碼
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