In the past I did a couple of blog posts (here and here) about how ASP.NET Web API discovers controllers.

ASP.NET MVC 6 supports both regular controllers (inheriting from Controller base type) and POCO controllers. Let’s have a look at how the discovery of them happens in ASP.NET MVC 6. Note that the code and mechanisms discussed in this article were introduced after ASP.NET 5 beta3 was released, so it is not yet available if you use the version of ASP.NET 5 bundled with Visual Studio 2015 CTP6.

Assemblies

Before we even get into looking at the Types that can become controllers, we need to understand how MVC selects assemblies it should look at.

Out of the box, this process is goverened by DefaultAssemblyProvider which dictates that MVC will look for controllers in any assembly that references any of the following MVC assemblies:

“Microsoft.AspNet.Mvc”,

“Microsoft.AspNet.Mvc.Core”,

“Microsoft.AspNet.Mvc.ModelBinding”,

“Microsoft.AspNet.Mvc.Razor”,

“Microsoft.AspNet.Mvc.Razor.Host”,

“Microsoft.AspNet.Mvc.TagHelpers”

This means that if you build an external library in which you want to have your controllers, they will be automatically discovered in any MVC application, since your external library will likely have a reference to “Microsoft.AspNet.Mvc” or any other of the assemblies mentioned above.

On the other hand, if you don’t reference any of the MVC assemblies – for example because you want to have external POCO controllers – they will not be discovered.

There are two ways of customizing the process of selecting candidate assemblies. You can implement a custom IAssemblyProvider (or override the DefaultAssemblyProvider). It’s a single method interface, where you can hint MVC which assemblies should be used:

C#

1

2

3

4

publicinterfaceIAssemblyProvider

{

IEnumerable<Assembly>CandidateAssemblies{get;}

}

There is a second way of doing it, probably a bit simpler. There is an extension method on IServicesCollection that allows you to specify which assemblies should be inspected:

C#

1

2

3

4

5

services.AddMvc().AddControllersAsServices(new[]

{

typeof(MyController).Assembly,

typeof(ExternalPocoController).Assembly

});

Under the hood, this replaces DefaultAssemblyProvider with a StaticAssemblyProvider, and as the name suggests, only defines specific assemblies that MVC should use to find controllers.

Types

Once MVC has the assemblies, it will need to determine which Types in those assemblies can become controllers. Similarly to assemblies discovery, this process is governed by a relevant interface – IControllerTypeProvider.

C#

1

2

3

4

publicinterfaceIControllerTypeProvider

{

IEnumerable<TypeInfo>ControllerTypes{get;}

}

The out-of-the box rules (defined in DefaultControllerTypeProvider, the default implementation of the above interface) for controllers to be discovered are the following:

has to be class

can’t be abstract

has to be public

has to be top-level (not nested)

can’t be generic

has to either derive from Controller base class or end in Controller suffix (for POCOs) and be located in an assembly that references MVC assembly (as mentioned in the previous point)

not be decorated with NonControllerAttribute (an opt out from discovery, for example it your class is not intended to be used by MVC but happens to be called in a way that might cause it to become discovered i.e. DomainController)

You could implement the provider yourself to customize this behavior, but just like it was the case for assemblies, there is an extension method that could help you to restrict MVC to a fixed set of controllers only and not discover anything by convention.

C#

1

2

3

4

5

services.AddMvc().AddControllersAsServices(new[]

{

typeof(MyController),

typeof(ExternalPocoController)

});

Under the hood, this replaces DefaultControllerTypeProvider with a StaticControllerTypeProvider, and as the name suggests, only defines specific (fixed set) controller types to be used as controllers.

“How ASP.NET MVC 6 discovers controllers” is a statement. No question mark required. For a question mark to be appropriate, it should be phrased as an actual question. “How does ASP.NET MVC 6 discover controllers?”

Ah that’s interesting. I had passively wondered about how the controller discovery worked. Seems like a good balance between exposing things you didn’t mean to and making discovery difficult. Thanks for writing

I made a Roles Manager (CRUD), however, when I remove a related roles with a User, it is still accessing the Controller, which in this case has [Authorize (Roles = “Admin”)].

http://tamangbinod.com Bakamaru Tamang

how to inject other dependencies on that assembly? I tried with external project dll but following error message was shown. System.IO.FileNotFoundException

Could not load file or assembly
‘file:///C:UsersbdtamDocumentsVisual Studio
2015ProjectsWebApplication4srcWebApplication4plugindnx451embeddedview.dll’
or one of its dependencies. The system cannot find the file specified.

Filip, thanks for the article. Can you clarify how assemblies are ‘discovered’? If my assembly references one of the above named namespaces but my assembly is not referenced by the web application, it is only bin deployed and the web application does not have a reference to it, will MV6 discover it?