MVC 2 Areas and Containers

Tuesday, October 20, 2009

Some projects use a container like StructureMap to completely replace MVC’s DefaultControllerFactory. They do by registering all controllers by name using StructureMap’s scanning feature during application startup.

ObjectFactory.Initialize(x =>
    x.Scan(s => {
        s.AssembliesFromPath("bin");
        s.AddAllTypesOf<IController>()
                 .NameBy(type =>
                     type.Name.Replace("Controller", ""));
    }));

A simple controller factory to make this work would look like this:

public class SimpleControllerFactory : 
    IControllerFactory
{
    public IController CreateController(
        RequestContext requestContext, string controllerName)
    {
        return 
            ObjectFactory.GetNamedInstance
                <IController>(controllerName);
    }

    // ...
}

As I mentioned in a previous post, using Areas in MVC 2 means you have the potential for multiple controllers with the same name. A Home controller in the parent project, and a Home controller in each sub-project, for example. Now the simple approach we are using in the code above breaks down. We could do some work to make sure we are registering and looking up types using the proper namespaces, but the logic to look at the namespace constraints is already embedded in MVC’s DefaultControllerFactory.

An easier approach is to use the DefaultControllerFactory to lookup controller types and only use StructureMap to instantiate the controller and resolve dependencies. Doing so means we don’t need to scan any assemblies. StructureMap (and most IoC frameworks) are capable of instantiating an unregistered type as long as the type is concrete. All we need to do is derive from DefaultControllerFactory and override the GetControllerInstance method.

public class BetterControllerFactory
    : DefaultControllerFactory 
{
    protected override IController GetControllerInstance(
        RequestContext requestContext, Type controllerType)
    {
        IController result = null;
        if (controllerType != null)
        {
            result = ObjectFactory.GetInstance(controllerType)
                as IController;
        }
        return result;                
    }
}

Moral of the story: Don’t automatically throw away the DefaultControllerFactory. You may find it has some conventions you can make use of!


Comments
Joseph Daigle Wednesday, October 21, 2009
You don't want to return null. Instead call and return the base implementation of the override if the ObjectFactory returns null. This way MVC will properly construct a 404 when the controller isn't found.
Scott Wednesday, October 21, 2009
@Joseph - thanks for the tip!
Comments are now closed.
by K. Scott Allen K.Scott Allen
My Pluralsight Courses
The Podcast!