Alexander Beletsky's development blog

My profession is engineering

Inside ASP.NET MVC: Instantiation of Controller

Controller are being created by ControllerFactory, by default IControllerFactory type is being resolved to DefaultControllerFactory. Today’s post is dedicated to some details of how actually DefaultControllerFactory works and creates instance of required controller. Let’s go from the beginning!

Request for controller instance

Initially, we are at MvcHandler’s ProcessRequestInit method, where we extract controllers name from RouteData and request controller factory to create corresponding controller.

private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {
 // If request validation has already been enabled, make it lazy. This allows attributes like [HttpPost] (which looks
 // at Request.Form) to work correctly without triggering full validation.
 bool? isRequestValidationEnabled = ValidationUtility.IsValidationEnabled(HttpContext.Current);
 if (isRequestValidationEnabled == true) {
  ValidationUtility.EnableDynamicValidation(HttpContext.Current);
 }

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

DefaultControllerBuilder internals

CreateController is rather elegant method, basically it does some arguments check’s, resolve the controller type by it’s name and then call to GetControllerIntance to instantiate that type.

public virtual IController CreateController(RequestContext requestContext, string controllerName) {
 if (requestContext == null) {
  throw new ArgumentNullException("requestContext");
 }
 if (String.IsNullOrEmpty(controllerName)) {
  throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
 }
 Type controllerType = GetControllerType(requestContext, controllerName);
 IController controller = GetControllerInstance(requestContext, controllerType);
 return controller;
}

Getting the controller type

GetControllerType is delegating it’s call to internal GetControllerTypeWithinNamespaces. It receives Route, Controller’s name and 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);
 }
}

The namespaces parameter are quite important. If you remember, you can explicitly mention the namespace during the route definition, with overloaded MapRoute method:

public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces) {
 return MapRoute(routes, name, url, null /* defaults */, null /* constraints */, namespaces);
}

Namespaces parameter then being stored to Route.DataTokens[“Namespaces”]. Namespaces parameter matters, then you have controllers with same names. This is in particular makes sense then you have different areas.

Caching Controller Types

The interesting thing is that framework not re-reads types for each request, that would be just to expesive, but instead it uses a cache which is initialized at the very first request and used at the life time of application. The call ControllerTypeCache.EnsureInitialized(BuildManager); makes sure that cache is in actual state. How does MVC caching the types?

Very simple and straight forward solution - in .XML file.

public static List<Type> GetFilteredTypesFromAssemblies(string cacheName, Predicate<Type> predicate, IBuildManager buildManager) {
 TypeCacheSerializer serializer = new TypeCacheSerializer();

 // first, try reading from the cache on disk
 List<Type> matchingTypes = ReadTypesFromCache(cacheName, predicate, buildManager, serializer);
 if (matchingTypes != null) {
  return matchingTypes;
 }

 // if reading from the cache failed, enumerate over every assembly looking for a matching type
 matchingTypes = FilterTypesInAssemblies(buildManager, predicate).ToList();

 // finally, save the cache back to disk
 SaveTypesToCache(cacheName, matchingTypes, buildManager, serializer);

 return matchingTypes;
}

It try’s read cache from file, if there are not matching types there it would try to read assemblies and then store it to cache. You might be interested where this file is actually located, so take a look:


cache file name

And for the most curious guys, here is the content:

<?xml version="1.0" encoding="utf-8"?>
<!--This file is automatically generated. Please do not modify the contents of this file.-->
<typeCache lastModified="14.10.2011 19:33:03" mvcVersionId="aa5414f4-4d8e-4f2a-a98b-7334bf15d104">
  <assembly name="MvcForDebug2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
    <module versionId="1ad99820-dc17-4be0-9f56-6dd2bdcd7950">
      <type>MvcForDebug2.Controllers.HomeController</type>
    </module>
  </assembly>
</typeCache>

FilterTypesInAssemblies method tries to get all controllers it can. What it does, it goes throught all referenced assemblies and using special predicate class matching the types.

private static IEnumerable<Type> FilterTypesInAssemblies(IBuildManager buildManager, Predicate<Type> predicate) {
 // Go through all assemblies referenced by the application and search for types matching a 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));
}

So, the last interesting thing here is the predicate that actually used:

internal static bool IsControllerType(Type t) {
 return
  t != null &&
  t.IsPublic &&
  t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
  !t.IsAbstract &&
  typeof(IController).IsAssignableFrom(t);
}

You can see, it tries to match any public, ends with “Controller” not abstract and implementing IController interface. That’s why important do not forget to call all your controller with “Controller” suffix (yes, I did that mistake several times in the beginning of my MVC journey).

Note, that if you have 2 controllers with same names in different namespaces, but did not provide namespace constraint you will have several matchingTypes, so the CreateAmbiguousControllerException will be thrown. I believe each of us seen that kind of exception at least once.

Instantiating the Type

As we go a little back and check the code of CreateController: now, we’ve got the type (or null if type has not been resolved). Next thing is to instantiate it. Nothing really fancy here:

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

If everything is all right with controller type it ask ControllerActivator to create the instance. In default case, ControllerActivator is 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);
 }
}

As for the rest of MVC entities it would use IDependencyResolver to resolve that type. IDependecyResolved is detailed here, but as you remember by default at the very end it calls Activator.CreateInstance(type) method.

Conclusions

New controller is instantiated on each HTTP request. The instantiation is in-directed, means first we retrieve Type, after creating Instance. The type is being searched dynamically by getting out reflection information from assemblies. To optimize the performance of ASP.NET MVC application, the internal cache of types is used. The cache is stored in file in “Temporary ASP.NET Files\root\cdd53039\36a27802\UserCache\MVC-ControllerTypeCache.xml” folder. Namespaces of controllers are matters, in case of two or more controllers has the same name but different namespaces the exception would be thrown. In case of controller type could not be resolved, the HTTP 404 response is generated. Otherwise, the controller instance would be created by IControllerActivator instance.

Previous post: Inside ASP.NET MVC: IDependencyResolver - Service locator in MVC