public class MvcHandler : IHttpHandler, IRequiresSessionState { [...] protected internal virtual void ProcessRequest(HttpContextBase httpContext) { AddVersionHeader(httpContext); // Get the controller type string controllerName = RequestContext.RouteData.GetRequiredString("controller"); // Instantiate the controller and call Execute IControllerFactory factory = ControllerBuilder.GetControllerFactory(); IController controller = factory.CreateController(RequestContext, controllerName); if (controller == null) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentUICulture, MvcResources.ControllerBuilder_FactoryReturnedNull, factory.GetType(), controllerName)); } try { controller.Execute(RequestContext); } finally { factory.ReleaseController(controller); } } [...] }
Firstly it reads the controller name from the routing data and after that it creates a controller by the name read before. Finally it calls the Execute method of the controller to run the controller’s code.
The ControllerBuilder is a factory for creating the controller instance.
public class ControllerBuilder { [...] private Func<IControllerFactory> _factoryThunk; public ControllerBuilder() { SetControllerFactory(new DefaultControllerFactory() { ControllerBuilder = this }); } [...] public IControllerFactory GetControllerFactory() { IControllerFactory controllerFactoryInstance = _factoryThunk(); return controllerFactoryInstance; } public void SetControllerFactory(IControllerFactory controllerFactory) { if (controllerFactory == null) { throw new ArgumentNullException("controllerFactory"); } _factoryThunk = () => controllerFactory; } [...] }The constructor of the class at line 8 calls the SetControllerFactory method with a DefaultControllerFactory instance. Thus the DefaultControllerFactory class will look up and call the controller with the name provided by the RouteData.
public class DefaultControllerFactory : IControllerFactory { private IBuildManager _buildManager; private ControllerBuilder _controllerBuilder; private ControllerTypeCache _instanceControllerTypeCache; private static ControllerTypeCache _staticControllerTypeCache = new ControllerTypeCache(); [...] public virtual IController CreateController(RequestContext requestContext, string controllerName) { [...] RequestContext = requestContext; Type controllerType = GetControllerType(controllerName); IController controller = GetControllerInstance(controllerType); return controller; } protected internal virtual IController GetControllerInstance(Type controllerType) { [...] try { return (IController)Activator.CreateInstance(controllerType); } catch (Exception ex) { throw new InvalidOperationException( String.Format( CultureInfo.CurrentUICulture, MvcResources.DefaultControllerFactory_ErrorCreatingController, controllerType), ex); } } protected internal virtual Type GetControllerType(string controllerName) { [...] // first search in the current route's namespace collection object routeNamespacesObj; Type match; [...] // then search in the application's default namespace collection [...] // if all else fails, search every namespace return GetControllerTypeWithinNamespaces(controllerName, null /* namespaces */); } private Type GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces) { // Once the master list of controllers has been created we can quickly index into it ControllerTypeCache.EnsureInitialized(BuildManager); IList<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces); switch (matchingTypes.Count) { case 0: // no matching types return null; case 1: // single matching type return matchingTypes[0]; default: // multiple matching types // we need to generate an exception containing all the controller types StringBuilder sb = new StringBuilder(); foreach (Type matchedType in matchingTypes) { sb.AppendLine(); sb.Append(matchedType.FullName); } throw new InvalidOperationException([...]); } } }As soon as the DefaultControllerFactory.CreateController function, it finds the type of the controller by its name and creates an instance after that. The framework uses the ControllerTypeCache class to find the controller types.
internal sealed class ControllerTypeCache { [...] public void EnsureInitialized(IBuildManager buildManager) { if (_cache == null) { lock (_lockObj) { if (_cache == null) { List<Type> controllerTypes = GetAllControllerTypes(buildManager); [...] } } } } private static List<Type> GetAllControllerTypes(IBuildManager buildManager) { // Go through all assemblies referenced by the application and search for // controllers and controller factories. List<Type> controllerTypes = new List<Type>(); ICollection assemblies = buildManager.GetReferencedAssemblies(); foreach (Assembly assembly in assemblies) { Type[] typesInAsm; try { typesInAsm = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { typesInAsm = ex.Types; } controllerTypes.AddRange(typesInAsm.Where(IsControllerType)); } return controllerTypes; } [...] internal static bool IsControllerType(Type t) { return t != null && t.IsPublic && t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) && !t.IsAbstract && typeof(IController).IsAssignableFrom(t); } }The ControllerTypeCache.EnsureInitialized (called by DefaultControllerFactory.GetControllerTypeWithinNamespaces) uses the BuildManager class (in MVC, wrapped by BuildManagerWrapper that implements the internal IBuildManager interface) to find the available assemblies. The ControllerTypeCache.GetAllControllerTypes returns the controller types referenced by the project. A class is a controller type if the class is
- public
- its name ends with “Controller”, casing is ignored
- the type is not abstract
- the type “implements” (can be assigned to) the IController interface.
The MvcHandler.ProcessRequest() calls the controller.Execute() to run the controller code.
Link: ASP.NET MVC – Finding the controller
No comments:
Post a Comment