Magellan Exceptions

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

Back to: Magellan Home

Because the Magellan framework handles many parts of the navigation lifecycle, there are a number of things that can go wrong. I want to dedicate this page to explaining the common exceptions that are thrown by Magellan, and look at techniques for handling them.

Exceptions thrown by Magellan

Where logical, Magellan throws standard .NET exceptions such as ArgumentNullException and InvalidOperationException. However, Magellan also has it's own set of custom exceptions that can be thrown. All custom Magellan exceptions derive from an abstract NavigationException class, which you can use if for some reason you want to handle all Magellan exceptions in a specific way (such as from a common exception policy handler).

NavigationConfigurationException

This is usually thrown if there is something in Magellan that has not been setup properly. This can be thrown when:

  • You forget to set the controller factory via ControllerBuilder.Current.SetControllerFactory when the application starts up. See the section on controllers and controller factories for more details.
  • There are no View Engines registered, because they have been cleared and none were added. By default the View Engines are automatically registered by Magellan, so the application must have code somewhere that clears the view engines for this to happen. See the topic on View Engines for more details.

ImpossibleNavigationRequestException

This is thrown when a navigation request has been made, but there is no logical way that Magellan could handle it. An example that causes this is when you use Navigator.Default to navigate to an action, and the action attempts to navigate to a page. Magellan can't navigate to the Page without knowing the NavigationService, so there is no way to fulfill this request. See the section on the Navigator for more details.

ActionNotFoundException

This exception is thrown when an action is not found on the controller. This usually indicates that the controller exists, but the name of the action is incorrect, or the wrong controller was specified. Generally, actions should be public, non-static/shared methods, and must be declared as returning ActionResult or a derived type.

UnhandledActionInvocationException

This is thrown when an action threw an exception, and it wasn't handled by any of the Action Filters. Some pseudo code for how actions are invoked is below:

try 
   call action()
catch ex
   ask any of the action filters to handle it
   if handled = true, do nothing
   else, throw UnhandledActionInvocationException(ex)

As you can probably guess, the reason for throwing a custom exception instead of re-throwing the original, is that Magellan would lose the stack trace, which would make tracking the real exception down very hard. So instead, Magellan wraps it in this exception. You can check the InnerException property to see the real cause for the exception.

As the code above suggests, Action Filters get a chance to handle exceptions. I'll talk more about that below.

UnhandledActionResultException

This guy is thrown when the action was executed fine, but when the ActionResult which it returned threw an exception. This can happen for example if you return a StartProcessResult, but the process name doesn't exist.

As with action invocation, the action filters will get a chance to see and handle this exception too. If they choose not to, this exception will be thrown, with the real exception nested safely inside the InnerException.

ViewNotFoundException

This hopefully self-explanatory exception is thrown when you return a Page, Window or Dialog action result, but the view name you specify doesn't exist. This exception also includes the names of all of the attempted search locations where Magellan looked for the view in the aptly-named SearchLocations property.

Techniques for Handling Exceptions

As with any exception handling guidance, the best approach is to avoid exceptions from happening in the first place. However, that's not always possible.

To avoid UnhandledActionInvocationException and UnhandledActionResultException, you can make use of Action Filters. The action filters section has more details.

Here is an example action filter that logs all navigation errors and shows a message box when they occur:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class HandleErrorsAttribute : Attribute, IActionFilter, IResultFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.Exception != null)
        {
            Trace.WriteLine(context.Exception);
            context.ExceptionHandled = true;
            MessageBox.Show(context.Exception.Message);
        }
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        if (context.Exception != null)
        {
            Trace.WriteLine(context.Exception);
            context.ExceptionHandled = true;
            MessageBox.Show(context.Exception.Message);
        }
    }
}

This can then be applied to a controller as simply as:

[HandleErrors]
public class MyController : Controller ...

Your filter can also override the exception - for example:

public void OnResultExecuted(ResultExecutedContext context)
{
    if (context.Exception is SqlException)
    {
        Trace.WriteLine(ex);
        context.Exception = new GenericException("A database issue occurred.");
    }
}

That said, for the most part if your intention is to just log and suppress all exceptions, you can do so via the Application.Current.DispatcherUnhandledException event without involving Magellan. There are occasions where this form of handling could come in handy however.

Back to: Magellan Home