在上一篇文章asp.net mvc源碼分析-路由篇 如何找到 IHttpHandler中最後提到了MvcHandler,來上我們看看它的定義
public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState
它有幾個比較重要的屬性:
internal ControllerBuilder ControllerBuilder ,ControllerBuilder 類主要負責建立IControllerFactory
public RequestContext RequestContext,RequestContext是對這次請求的HttpContext(httpContext類)和RouteData的封裝
而它的BeginProcessRequest方法中有幾句比較重要的代碼:
IControllerFactory factory;
ProcessRequestInit(httpContext, out controller, out factory);
IAsyncController asyncController = controller as IAsyncController;
if (asyncController != null) {
}
else
{
Action action = delegate {
try {
controller.Execute(RequestContext);
}
finally {
factory.ReleaseController(controller);
}
return AsyncResultWrapper.BeginSynchronous(callback, state, action, _processRequestTag);
}
在這篇文章中我們主要關心ProcessRequestInit(httpContext, out controller, out factory); 如何建立controller
ProcessRequestInit方法大致如下:
private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
factory = ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(RequestContext, controllerName);
}
string controllerName = RequestContext.RouteData.GetRequiredString("controller");這句很簡單擷取Controller的名稱,RouteData是當前路由資料,裡面的key包括我們註冊是的url格式 "{controller}/{action}/{id}", 和預設的defaults=new { controller = "Home", action = "Index", id = UrlParameter.Optional }
現在大家知道為什麼了我上一篇文章提到{controller}/{action}是必須得了吧。
現在讓我們來看看ControllerBuilder的主要代碼:
public ControllerBuilder() : this(null) { } internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver) { _serviceResolver = serviceResolver ?? new SingleServiceResolver<IControllerFactory>( () => _factoryThunk(), new DefaultControllerFactory { ControllerBuilder = this }, "ControllerBuilder.GetControllerFactory" ); } public static ControllerBuilder Current { get { return _instance; } } public IControllerFactory GetControllerFactory() { return _serviceResolver.Current; } public void SetControllerFactory(IControllerFactory controllerFactory) { if (controllerFactory == null) { throw new ArgumentNullException("controllerFactory"); } _factoryThunk = () => controllerFactory; } public void SetControllerFactory(Type controllerFactoryType) { if (controllerFactoryType == null) { throw new ArgumentNullException("controllerFactoryType"); } if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType)) { throw new ArgumentException( String.Format( CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_MissingIControllerFactory, controllerFactoryType), "controllerFactoryType"); } _factoryThunk = delegate() { try { return (IControllerFactory)Activator.CreateInstance(controllerFactoryType); } catch (Exception ex) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, MvcResources.ControllerBuilder_ErrorCreatingControllerFactory, controllerFactoryType), ex); } }; }
由以上代碼我們可以知道如果沒有調用ControllerBuilder的SetControllerFactory方法那我們就用預設的DefaultControllerFactory,如果設定了我們就可以用我們自己ControllerFactory。如果項目有需要實現自己的ControllerFactory,那麼我們可以Global.asax.cs的 protected void Application_Start()方法調用 ControllerBuilder.Current.SetControllerFactory(xxx);
讓我們來看看DefaultControllerFactory是怎麼建立的:
internal DefaultControllerFactory(IControllerActivator controllerActivator, IResolver<IControllerActivator> activatorResolver, IDependencyResolver dependencyResolver) {
if (controllerActivator != null) {
_controllerActivator = controllerActivator;
}
else {
_activatorResolver = activatorResolver ?? new SingleServiceResolver<IControllerActivator>(
() => null,
new DefaultControllerActivator(dependencyResolver),
"DefaultControllerFactory contstructor"
);
}
}
這裡我們涉及到一個IControllerActivator介面,預設情況下用的是DefaultControllerActivator類,
現在我們已經得到了ControllerFactory,再看看它是怎麼建立Controller的,說白了就是DefaultControllerFactory的CreateController方法
public virtual IController CreateController(RequestContext requestContext, string controllerName) {
。。
Type controllerType = GetControllerType(requestContext, controllerName);
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
}
預設我們是沒有namespaces,而GetControllerType(requestContext, controllerName)主要是調用 GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */)
private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces) { // Once the master list of controllers has been created we can quickly index into it ControllerTypeCache.EnsureInitialized(BuildManager); ICollection<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces); switch (matchingTypes.Count) { case 0: // no matching types return null; case 1: // single matching type return matchingTypes.First(); default: // multiple matching types throw CreateAmbiguousControllerException(route, controllerName, matchingTypes); } }
這裡面主要涉及到一個ControllerTypeCache,它負責緩衝當前程式中所有的Controller。
讓我們看看ControllerTypeCache的主要方法吧
public void EnsureInitialized(IBuildManager buildManager) { if (_cache == null) { lock (_lockObj) { if (_cache == null) { List<Type> controllerTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsControllerType, buildManager); var groupedByName = controllerTypes.GroupBy( t => t.Name.Substring(0, t.Name.Length - "Controller".Length), StringComparer.OrdinalIgnoreCase); _cache = groupedByName.ToDictionary( g => g.Key, g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); } } } } internal static bool IsControllerType(Type t) { return t != null && t.IsPublic && t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) && !t.IsAbstract && typeof(IController).IsAssignableFrom(t); } public ICollection<Type> GetControllerTypes(string controllerName, HashSet<string> namespaces) { HashSet<Type> matchingTypes = new HashSet<Type>(); ILookup<string, Type> nsLookup; if (_cache.TryGetValue(controllerName, out nsLookup)) { // this friendly name was located in the cache, now cycle through namespaces if (namespaces != null) { foreach (string requestedNamespace in namespaces) { foreach (var targetNamespaceGrouping in nsLookup) { if (IsNamespaceMatch(requestedNamespace, targetNamespaceGrouping.Key)) { matchingTypes.UnionWith(targetNamespaceGrouping); } } } } else { // if the namespaces parameter is null, search *every* namespace foreach (var nsGroup in nsLookup) { matchingTypes.UnionWith(nsGroup); } } } return matchingTypes; }
List<Type> controllerTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsControllerType, buildManager);這句代碼就是用來查詢當前程式的類,這個類必須通過IsControllerType方法的驗證(類是共有非抽象繼承於IController且類名Controller結尾),而這個_cache是個Dictionary<string, ILookup<string, Type>>類型的資料。
現在我們已經得到了ControllerType,來看看 IController controller = GetControllerInstance(requestContext, controllerType);吧裡面主要就一句
ControllerActivator.Create(requestContext, controllerType);而ControllerActivator就是先前的DefaultControllerActivator,DefaultControllerActivator的代碼如下:
private class DefaultControllerActivator : IControllerActivator { 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); } } }
這裡的_resolverThunk()來源於DependencyResolver.Current,
private static DependencyResolver _instance = new DependencyResolver();
public static IDependencyResolver Current {
get {
return _instance.InnerCurrent;
}
}
private IDependencyResolver _current = new DefaultDependencyResolver();
public IDependencyResolver InnerCurrent {
get {
return _current;
}
}
所以真正建立controller是在DefaultDependencyResolver的GetService方法,核心代碼 return Activator.CreateInstance(serviceType);
這篇檔案扯得比較遠,可見建立Controller是多麼的麻煩啊。閱讀原始碼主要是讓我們理解其邏輯,同時也讓我們學習其開發的思想。
本文講的這麼多,在項目中最常用的就是ControllerBuilder.Current.SetControllerFactory(xxx); 只要大家理解了它就可以了。