protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) { if (requestContext == null) { throw new ArgumentNullException("requestContext"); } if (String.IsNullOrEmpty(controllerName) && (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch())) { throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName"); } RouteData routeData = requestContext.RouteData; if (routeData != null && routeData.HasDirectRouteMatch()) { return GetControllerTypeFromDirectRoute(routeData); } // first search in the current route's namespace collection object routeNamespacesObj; Type match; if (routeData.DataTokens.TryGetValue(RouteDataTokenKeys.Namespaces, out routeNamespacesObj)) { IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>; if (routeNamespaces != null && routeNamespaces.Any()) { HashSet<string> namespaceHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase); match = GetControllerTypeWithinNamespaces。 (routeData.Route, controllerName, namespaceHash); // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true" if (match != null || false.Equals(routeData.DataTokens[RouteDataTokenKeys.UseNamespaceFallback])) { // got a match or the route requested we stop looking return match; } } } // then search in the application's default namespace collection if (ControllerBuilder.DefaultNamespaces.Count > 0) { HashSet<string> namespaceDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase); match = GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaceDefaults); if (match != null) { return match; } } // if all else fails, search every namespace return GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null /* namespaces */); }
首先进行了入口的检查,HasDirectRouteMatch这个方法是用来判断这个RouteData是否是特性路由?应该是这个做用。如今还没知道,之后说WebApi的时候可能会说到。全部默认状况是不会去执行GetControllerTypeFromDirectRoute的方法的。在Route中获取命名空间,若是设置了NameSpace的话便在这边取出,而后转化为List
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); } }
GetControllerTypeWithinNamespaces里面就就两句,经过BuildManager初始化,ControllerTypeCache看这个名字也知道是控制器类型缓存,而后经过名字和namesapces得到Type,若是0个返回null,1个正确,2个便报错。mvc
主要看一下EnsureInitialized和GetControllerTypes这两个方法。app
public void EnsureInitialized(IBuildManager buildManager) { if (_cache == null) { lock (_lockObj) { if (_cache == null) { //TypeCaheName='MVC-ControllerTypeCache.xml' 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); } } } }
里面又调用了TypeCacheUtil的GetFilteredTypesFromAssemblies的方法。获得以后,对控制器的名字进行截取,取控制前的名字进行分组(GroupBy)。而后封装成一个字典_cache。而后咱们看看GetFilteredTypesFromAssemblies这个方法里面发生了什么。ui
//predicate == IsControllerType public static List<Type> GetFilteredTypesFromAssemblies(string cacheName, Predicate<Type> predicate, IBuildManager buildManager) { TypeCacheSerializer serializer = new TypeCacheSerializer(); // 首先从磁盘文件中读取数据 cacheName='MVC-ControllerTypeCache.xml' List<Type> matchingTypes = ReadTypesFromCache(cacheName, predicate, buildManager, serializer); if (matchingTypes != null) { return matchingTypes; } // 若是读到数据,将每一个数据进行对比 matchingTypes = FilterTypesInAssemblies(buildManager, predicate).ToList(); // 最后保存会磁盘中 SaveTypesToCache(cacheName, matchingTypes, buildManager, serializer); return matchingTypes; }
在c盘应该能够找到和这个文件,这就是用来进行缓存mvc和Action的类型。里面的细节的逻辑就不看。主要看这个方法FilterTypesInAssemblies,方法的意思应该就是在程序集中过滤Type。spa
//predicate == IsControllerType private static IEnumerable<Type> FilterTypesInAssemblies(IBuildManager buildManager, Predicate<Type> predicate) { // 浏览应用的全部程序集,并进行谓语匹配 IEnumerable<Type> typesSoFar = Type.EmptyTypes; ICollection assemblies = buildManager.GetReferencedAssemblies(); foreach (Assembly assembly in assemblies) { Type[] typesInAsm; try { typesInAsm = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { typesInAsm = ex.Types; } typesSoFar = typesSoFar.Concat(typesInAsm); } return typesSoFar.Where(type => TypeIsPublicClass(type) && predicate(type)); }
定义了一个空的类型,经过buildManager.GetReferencedAssemblies()得到引用的程序集。循环,获取每一个程序集的type,在最后的时候进行条件过滤,在最前面标注了predicate的值是IsControllerType。这个值是定义在ControllerTypeCache中的。code
internal static bool IsControllerType(Type t) { return t != null && t.IsPublic && t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) && !t.IsAbstract && typeof(IController).IsAssignableFrom(t); }
就是说这个Type是否是public,是否是已Controller结尾,要不是抽象类,是否是继承IController。经过的Type放到typesSoFar。而后返回matchingTypes,而后赋给controllerTypes。而后赋值给_cache。 最后进行路由匹配。orm
public ICollection<Type> GetControllerTypes(string controllerName, HashSet<string> namespaces) { HashSet<Type> matchingTypes = new HashSet<Type>(); ILookup<string, Type> namespaceLookup; if (_cache.TryGetValue(controllerName, out namespaceLookup)) { // 若是命名空间不为空的话,循环Type比较Type的命名空间是否和设置的一致。 if (namespaces != null) { foreach (string requestedNamespace in namespaces) { foreach (var targetNamespaceGrouping in namespaceLookup) { if (IsNamespaceMatch(requestedNamespace, targetNamespaceGrouping.Key)) { matchingTypes.UnionWith(targetNamespaceGrouping); } } } } else { // 若是命名空间为空的话,搜索全部的namespace foreach (var namespaceGroup in namespaceLookup) { matchingTypes.UnionWith(namespaceGroup); } } } return matchingTypes; }
调用了GetController的方法。这里才是真正的比对控制器的。首先创建了一个HashSet 泛型 的对象。经过controllerName的值在_cache中获取,有值,判断命名空间又没有,有的话,循环每一个命名空间。比对分完组的Controller集合。调用UnionWith的方法。这个是HashSet里面的方法。在集合中进行比较。若是没有重复就放进去。因此到最后返回的是一个Type的集合。
理一下上面的逻辑。xml
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); }
再次判断一下Type。其实最后起做用的只有最后一句。首先要看一下ControllerActivator这个属性是什么样的。对象
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 constructor"); } } private IControllerActivator ControllerActivator { get { if (_controllerActivator != null) { return _controllerActivator; } _controllerActivator = _activatorResolver.Current; return _controllerActivator; } }
能够看出是_activatorResolver.Current. _activatorResolver这个类型是何时初始化的呢?是在DefaultControllerFactory建立的时候,能够看到又是使用了SingleServiceResolver,这个是ControllerFactory的同样的套路,若是DependencyResolver中注册了对应的Resolver能够返回IControllerActivator就会首先返回这个。若是没有设置的话也就是默认状态的话会使用DefaultControllerActivator。继承
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); } }
若是构造DefaultControllerActivator的参数dependencyResolver为空,那就经过反射进行构造返回就好了。