1

Will's Error handling post is awesome! I'm not yet using ELMAH, but I'd like to start adding some of my own data to the Exception on a per Controller basis. Right now, I have a base controller overriding OnException(ExceptionContext) as per Will from which all project Controllers inherit.

I think I understand I'll need to customize another OnException override on each Controller, but how do I pass data into that Exception? The data I need may not always be a single text value.

  • Should I throw custom Exceptions?
  • Should I throw generic Exception(string) exceptions and somehow access them within the override?

Edit

This may sound silly, but is there any way to handle an unhandled error and still mine this information? If not, the only solution I can see is creating the custom Exception instance at the beginning of every Controller method for the remote possibility that it will be needed. Is this the only way?

Another Edit

Per Lost In Tangent's post (and part 2), I modified this CustomFactory class, and registered it in Global.asax. The references in his post to base.requestContext weren't valid.

public class CustomFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    {
        var controller = (Controller)base.GetControllerInstance(requestContext, controllerType);
        controller.ActionInvoker =
            new CustomControllerActionInvoker(new ControllerContext(requestContext, controller));
        return controller; 
    }
}

Then in my CustomControllerActionInvoker class, I override InvokeAction:

public override bool InvokeAction(ControllerContext controllerContext, string actionName)
{
    try
    {
        return base.InvokeAction(controllerContext, actionName);
    }
    catch (Exception)
    {
        throw;
    }
}

I think the catch block is where Brian suggests I create a ViewResult, but how do I create the right kind based on which controller it came from?

Community
  • 1
  • 1
David Fox
  • 10,603
  • 9
  • 50
  • 80
  • Can you give an example of what kind of data you would like to add to your exceptions, and what you plan to do with that data? Otherwise, custom exceptions sounds like the textbook choice. – bzlm Aug 18 '10 at 16:16
  • I like the idea of adding data to an exception, but in an MVC application, what would you do with it? Log it? Wouldn't a string suffice for that? – Robert Harvey Aug 18 '10 at 16:17
  • @bzlm If I'm in my Profile Controller and an error is thrown, I might throw the Exception with the entire Profile object in question. I'd at least like to get a type and some kind of ID data which all my models have. – David Fox Aug 18 '10 at 19:18
  • @Robert I'm thinking about unanticipated crashes shortly after production. I want an error log that gives more than just the stack trace. Will's post gave me the ability to make it crash gracefully and send the user to an Error view with error data if necessary – David Fox Aug 18 '10 at 19:20
  • You can capture the `InnerException` property of an exception to get more information about what happened; there is also a stack trace in there somewhere. – Robert Harvey Aug 18 '10 at 21:28

1 Answers1

1

If you want to add additional information to the logged message, create a custom exception and add additional properties for information you want to collect. Some loggers log the ToString() method return call, or only log the message, so I don't know what ELMAH does. Most probably do ToString(), so if in the exception you override ToSTring() to add the additional information there, that should be added to the message logged.

Brian Mains
  • 50,520
  • 35
  • 148
  • 257
  • This may sound silly, but is there any way to handle an unhandled error and still mine this information? If not, the only solution I can see is creating the custom Exception instance at the beginning of every Controller method for the remote possibility that it will be needed. Is this the only way? – David Fox Aug 18 '10 at 21:02
  • OK, I don't think I may have completely understood the problem. One solution may be to create a custom action invoker that inherits from ControllerActionInvoker. This object is responsible for invoking every action method. Override invoke action, call base.InvokeAction, and then wrap this call in a try/catch block to catch any excpetions thrown. That is one global way. – Brian Mains Aug 19 '10 at 02:04
  • This is great :) I didn't go as far as ControllerActionInvoker, but I did override OnException in my base Controller class and make a custom Exception for errors I throw myself. I also used the ExceptionContext.ActionParameters to get the .ToString() methods of the model objects being passed. That's primarily what I was after. I'm trying to figure out how to return the user to the proper View without removing the ELMAH logging ability. – David Fox Aug 19 '10 at 22:07
  • Hey, well, in the action invoker, you can then catch the exception, then create a ViewResult of the same view, pass in a working model, and call InvokeActionResult, which invokes the result as if the action worked fine. – Brian Mains Aug 20 '10 at 01:40
  • I found this article (http://lostintangent.com/2008/07/03/aspnet-mvc-controlleractioninvoker-part-1/) and the second part, but I'm not sure exactly what you're suggesting go into my CustomControllerActionInvoker.InvokeAction override. I'll update my post with what I have so far. I'm not sure how to "create a ViewResult of the same view, pass in a working model, and call InvokeActionResult" – David Fox Aug 24 '10 at 14:55
  • Yes, in your custom InvokeAction, if you call base.InvokeAction within a try{} catch {}, this will catch all errors that occur within a controller. But that action then failed, so you have to redirect them somewhere. The process of redirecting them somewhere involves creating a ViewResult instance and calling InvokeActionResult(); however, I'm not sure how you are going to make the redirection process seamless as if nothing has happened... not saying it's impossible, but it may be difficult in some cases, especially since views typically rely on the model. – Brian Mains Aug 24 '10 at 16:05
  • I have some action methods that simply return `Content("Ok")` and others than return `View(customViewModel)`. Can you provide a quick example of each? InvokeAction returns `bool`. Do I somehow call InvokeActionMethod to render the ActionResult and return `true`? Do I somehow use CreateActionResult? Any recommendation on readings to better understand this process? – David Fox Aug 24 '10 at 19:51
  • I don't know of any readings that better understand the process... I read the source code. That's what I'm trying to say, I don't see how you can just redirect the user back to the page as if nothing happened, because of the complexity of the different responses, and because of the model each view needs. But again, I'm not 100% sure. Sorry I can't be of more help... – Brian Mains Aug 25 '10 at 11:30