Controller activation and dependency in ASP.NET Core MVC

In this post, I’ll describe how controllers are created in ASP.NET Core MVC using the IControllerActivator, the options available out of the box, and their differences when it comes to dependency injection.

The default IControllerActivator

In ASP.NET Core, when a request is received by the MvcMiddleware, routing – either conventional or attribute routing – is used to select the controller and action method to execute. In order to actually execute the action, the MvcMiddleware must create an instance of the selected controller.

The process of creating the controller depends on a number of different provider and factory classes, culminating in an instance of the IControllerActivator. This class implements just two methods:

As you can see, the IControllerActivator.Create method is passed a ControllerContext which defines the controller to be created. How the controller is created depends on the particular implementation.

Out of the box, ASP.NET Core uses the DefaultControllerActivator, which uses the TypeActivatorCache to create the controller. The TypeActivatorCache creates instances of objects by calling the constructor of the Type, and attempting to resolve the required constructor argument dependencies from the DI container.

This is an important point. The DefaultControllerActivator doesn’t attempt to resolve the Controller instance from the DI container itself, only the Controller’s dependencies.

Example of the default controller activator

To demonstrate this behaviour, I’ve created a simple MVC application, consisting of a single service, and a single controller. The service instance has a name property, that is set in the constructor. By default, it will have the value "default".

 The HomeController for the app takes a dependency on the TestService, and returns the Name property:

 The final piece of the puzzle is the Startup file. Here I register the TestService as a scoped service in the DI container, and set up the MvcMiddleware and services:

You’ll also notice I’ve defined a factory method for creating an instance of the HomeController. This registers the HomeController type in the DI container, injecting an instance of the TestService with a custom Name property.

So what do you get if you run the app?

asw

As you can see, the TestService.Name property has the default value, indicating the TestService instance has been sourced directly from the DI container. The factory method we registered to create the HomeController has clearly been ignored.

This makes sense when you remember that the DefaultControllerActivator is creating the controller. It doesn’t request the HomeController from the DI container, it just requests its constructor dependencies.

Most of the time, using the DefaultControllerActivator will be fine, but sometimes you may want to create your controllers by using the DI container directly. This is especially true when you are using third-party containers with features such as interceptors or decorators.

Luckily, the MVC framework includes an implementation of IControllerActivator to do just this, and even provides a handy extension method to enable it.

The ServiceBasedControllerActivator

As you’ve seen, the DefaultControllerActivator uses the TypeActivatorCache to create controllers, but MVC includes an alternative implementation, the ServiceBasedControllerActivator, which can be used to directly obtain controllers from the DI container. The implementation itself is trivial:

 You can configure the DI-based activator with the AddControllersAsServices() extension method, when you add the MVC services to your application:

 With this in place, hitting the home page will create a controller by loading it from the DI container. As we’ve registered a factory method for the HomeController, our custom TestService configuration will be honoured, and the alternative Name will be used:

non-defaultThe AddControllersAsServices method does two things – it registers all of the Controllers in your application with the DI container (if they haven’t already been registered) and replaces the IControllerActivator registration with the ServiceBasedControllerActivator:

If you need to do something esoteric, you can always implement IControllerActivator yourself, but I can’t think of any reason that these two implementations wouldn’t satisfy all your requirements!

Summary

  • By default, the DefaultControllerActivator is configured as the IControllerActivator for ASP.NET Core MVC.
  • The DefaultControllerActivator uses the TypeActivatorCache to create controllers. This creates an instance of the controller, and loads constructor arguments from the DI container.
  • You can use an alternative activator, the ServiceBasedControllerActivator, which loads controllers directly from the DI container. You can configure this activator by using the AddControllersAsServices() extension method on the MvcBuilder instance in Startup.ConfigureServices.