views:

72

answers:

2

I have an ASP.NET MVC 2 application with a custom StructureMap controller factory to handle dependency injection for my controllers:

public class StructureMapControllerFactory : DefaultControllerFactory
{
    public override IController CreateController(RequestContext context, string controllerName)
    {
        Type controllerType = base.GetControllerType(context, controllerName);
        return ObjectFactory.GetInstance(controllerType) as IController;
    }
}

I would like to know how I can handle exceptions in this controller factory so that they can be redirected to the ~/Views/Shared/Error.aspx in the same way as they are in a controller that has the HandleError attribute. Currently exceptions don't do this despite having the CustomErrors attribute set to "On".

At the moment I can generate such an exception using a URL like "~/DoesNotExist/edit/1". With the default route:

routes.MapRoute(
    "Default", // Route name
    "{controller}/{action}/{id}", // URL with parameters
    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);

MVC matches this route and passes controller name "DoesNotExist" to my controller factory. The GetControllerType then returns null and causes a null reference exception in the call to StructureMap. I would then like to be able to handle this exception.

Note that adding a subsequent catch all route will not resolve this problem - MVC matches the default route.

I know I could solve this particular problem by putting constraints on the default route for controller but the question is more general about how I can use the normal MVC ~/Views/Shared/Error.aspx in the factory.

Note that I don't want the answer to require tight coupling of the controller factory to the particular MVC application. Ideally this factory should be in a referenced assembly not in the same solution.

A: 

I would create an Error controller that gets called by the controller factory if it cannot find the requested controller. Then within that, override the HandleUnknownAction to capture whatever action is being requested.

public class StructureMapControllerFactory : DefaultControllerFactory
{
    public override IController CreateController(RequestContext context, string controllerName)
    {
        Type controllerType = base.GetControllerType(context, controllerName);

        if (controllerType == null)
            return ObjectFactory.GetInstance(ErrorController) as IController;
        else
            return ObjectFactory.GetInstance(controllerType) as IController;
    }
}
Clicktricity
Yep, that'd do it, although this does mean the assembly with the factory in needs to also contain the error controller. That is not a disaster but I'd like to separate concerns if I can.
Rob West
In my own projects, I point to the Home controller and let the error handler there take over. Not entirely separate, but likely that most of your projects will have a HomeController.
Clicktricity
Nice, yes that's a pretty safe convention
Rob West
+1  A: 

Maybe you can adapt this controller factory to handle the exceptions.

cottsak
Yep, thanks for pointing me at that - a very nice solution to the problem.
Rob West