深入ASP.NET MVC之三:Controller的激活

上文说到Routing Module将控制权交给了MvcHandler,因为MvcHandler实现了IHttpAsyncHandler接口,因此紧接着就会调用 BeginProcessRequest方法,这个方法首先会进行一些Trust Level之类的安全检测,暂且不谈,然后会调用ProcessRequestInit方法(有删节):

双击代码全选

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {  

          // 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的名字,然后会实例化controller,这里采用了抽象工厂的模式,首先利用ControllerBuilder获得一 个IControllerFactory的实例,ControllerBuilder采用Dependency Injection来实例化IControllerFactory,关于MVC中DI的实现以后另文介绍,在默认情况 下,ControllerBuilder会返回一个实例。接着调用CreateController方法:

双击代码全选

1

2

3

4

5

public virtual IController CreateController(RequestContext requestContext, string controllerName) {  

    Type controllerType = GetControllerType(requestContext, controllerName);  

    IController controller = DefaultControllerFactoryGetControllerInstance(requestContext, controllerType);  

    return 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

protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName) {  

    // first search in the current route's namespace collection  

    object routeNamespacesObj;  

    Type match;  

    if (requestContext != null && requestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) {  

        IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;  

        if (routeNamespaces != null && routeNamespaces.Any()) {  

            HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);  

            match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsHash);  

        

            // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"  

            if (match != null || false.Equals(requestContext.RouteData.DataTokens["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> nsDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);  

        match = GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, nsDefaults);  

        if (match != null) {  

            return match;  

        }  

    }  

    // if all else fails, search every namespace  

    return GetControllerTypeWithinNamespaces(requestContext.RouteData.Route, controllerName, null /* namespaces */);  

}

 

DefaultControllerFactory 在根据路由信息查找对应Controller的类型的时候,首先判断DataToken中有没有Namespace,然后调用 GetControllerTypeWithinNamespaces 方法查找Controller对应的类。先看下这个方法:

双击代码全选

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

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);  

    }  

}

 

ASP.NET 中大量的用到了反射,因此也需要把这些反射出的类进行缓存以提高性能,首先看下EnsureInitialized这个比较有意思的方法,这个方法的参数 BuildManager经过了层层包装,其实只是System.Web.Compilation.BuildManager的一个实例。

双击代码全选

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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);  

                    }  

                }  

            }  

        }

 

首先TypeCacheUtil获得所有是Controller的类型。TypeCacheUtil在前文已经出现过,用来获取所有的AreaRegistration的子类型,这里仔细看下这个方法: