MVC 2 Areas and Containers

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!

Print | posted @ Tuesday, October 20, 2009 9:12 PM

Comments on this entry:

Gravatar # re: MVC 2 Areas and Containers
by Joseph Daigle at 10/21/2009 8:53 AM

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.
  
Gravatar # re: MVC 2 Areas and Containers
by Scott at 10/21/2009 10:39 AM

@Joseph - thanks for the tip!
  

Your comment:

Title:
Name:
Email:
Website:
 
Italic Underline Blockquote Hyperlink
 
 
Please add 5 and 3 and type the answer here: