How to register a Controller into ASP.NET MVC when the controller class is in a different assembly?

quakkels picture quakkels · Sep 26, 2011 · Viewed 24.1k times · Source

My goal is to modify asp.net mvc's controller registery so that I can create controllers and views in a separate (child) assembly, and just copy the View files and the DLLs to the host MVC application and the new controllers are effectively "Plugged In" to the host app.

Obviously, I will need some sort of IoC pattern, but I'm at a loss.

My thought was to have a child assembly with system.web.mvc referenced and then to just start building controller classes that inherited from Controller:

Separate Assembly:

using System.Web;
using System.Web.Mvc;

namespace ChildApp
{
    public class ChildController : Controller
    {
        ActionResult Index()
        {
            return View();
        }
    }
}

Yay all fine and dandy. But then I looked into modifying the host application's Controller registry to load my new child controller at runtime, and I got confused. Perhaps because I need a deeper understanding of C#.

Anyway, I thought I needed to create a CustomControllerFactory class. So I started writing a class that would override the GetControllerInstance() method. As I was typing, intellisence popped this up:

Host MVC Application:

public class CustomControllerFactory : DefaultControllerFactory 
{
    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        return base.GetControllerInstance(requestContext, controllerType);
    }
}

Now, at this point I'm at a loss. I don't know what that does. Originally, I was going to write a "Controller Loader" class like a Service Locator like this:

Host MVC Application:

public class ControllerLoader
{
    public static IList<IController> Load(string folder)
    {
        IList<IController> controllers = new List<IController>();

        // Get files in folder
        string[] files = Directory.GetFiles(folder, "*.plug.dll");
        foreach(string file in files)
        {
            Assembly assembly = Assembly.LoadFile(file);
            var types = assembly.GetExportedTypes();
            foreach (Type type in types)
            {
                if (type.GetInterfaces().Contains(typeof(IController)))
                {
                    object instance = Activator.CreateInstance(type);
                    controllers.Add(instance as IController);
                }
            }
        }
        return controllers;
    }
}

And then I was planning on using my list of controllers to register them in the controller factory. But ... how? I feel like I'm on the edge of figuring it out. I guess it all bois down to this question: How do I hook into return base.GetControllerInstance(requestContext, controllerType);? Or, should I use a different approach altogether?

Answer

Mark Seemann picture Mark Seemann · Sep 26, 2011

Reference the other assembly from the 'root' ASP.NET MVC project. If the controllers are in another namespace, you'll need to modify the routes in global.asax, like this:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
        namespaces: new[] { typeof(HomeController).Namespace }
    );

}

Notice the additional namespaces argument to the MapRoute method.