MVVM Light Activities and Exceptions

You are looking at revision 1 of this page, which may be out of date. View the latest version.

As part of my contrib project to add support for coroutines to MVVM Light, I've been thinking about how to handle exceptions.

Let's assume we have some code like this in an ActivityCommand handler method:

IEnumerable<IActivity> GetUnicorns()
{
    yield return new BusyIndicator(true); // show that we're busy

    yield return new UnicornDownloader(); // get the unicorns!

    yield return new BusyIndicator(false); // we're no longer busy
}

Great! We use a BusyIndicator activity to somehow notify the user that we're busy (heck, maybe it's just changing the cursor to an hourglass), then we kick off a UnicornDownloader activity to fetch the unicorns we needed. When it completes, we tell the user that we're not busy anymore.

However, there's a problem with this code. Unicorns aren't real! They're fantasy creatures! The UnicornDownloader activity is going to throw an AnimalNotFoundException!

That means one of two things will happen, depending on how well-written our UnicornDownloader activity is:

  1. The activity will raise the Completed event with the ActivityCompletedEventArgs.Error property set to an AnimalNotFoundException instance, and the processor will stop running the sequence.

  2. The activity will simply throw an exception, which we're not handling, causing the application to die.

Either way, the third activity in the sequence (letting the user know that we're not busy anymore) will never be executed. Right now there is no way to convey the exception information back to the calling method.

So what to do? Caliburn handles this situation in a unique way: It looks for a method on your ViewModel flagged as a "rescue" method and passes the execption to that. MVVM Light is, at its core, a little more explicit, and doesn't depend on special methods or anything. It's supposed to be a very lightweight, "dumb" approach to MVVM.

Right now there are two ways I can approach this problem:

Throw the Exception

Firstly, I could change the ActivityProcessor so it throws any exception returned as part of an activity's Completed event. Then you would have to change your GetUnicorns method to look more like this:

IEnumerable<IActivity> GetUnicorns()
{
    yield return new BusyIndicator(true); // show that we're busy

    try
    {
        yield return new UnicornDownloader(); // get the unicorns!
    }
    catch(AnimalNotFoundException ex)
    {
        // log the exception, maybe notify the user
    }
    finally
    {
        yield return new BusyIndicator(false); // we're no longer busy
    }
}

This is a bit more work on the part of the developer, but it's probably more predictable in that exceptions are never "swallowed" by the ActivityProcessor.

Continue Execution

Secondly, I could change the IActivity interface to look like this:

public interface IActivity
{
    Exception Error { get; set; }  // <-- add this

    void Execute(ActivityContext context);

    event EventHandler<ActivityCompletedEventArgs> Completed;
}

So when the ActivityProcessor sees an exception (whether it was unexpectedly thrown by the activity or returned as part of the Completed event) it could set the Error property on the activity and continue execution. The GetUnicorns method would now look more like this:

IEnumerable<IActivity> GetUnicorns()
{
    yield return new BusyIndicator(true); // show that we're busy

    var loader = new UnicornDownloader();
    yield return loader; // get the unicorns!

    if (loader.Error != null)
    {
        // log the exception, maybe notify the user
    }

    yield return new BusyIndicator(false); // we're no longer busy
}

Again, the developer would have to know that an activity might throw (or return) an exception, but at least he'd find out about it in a way that would let the execution continue if he so desired.

Your Thoughts?

So, should we just throw the exception no matter what? Or should we continue execution and let the developer know via an Error property on the IActivity interface? Let me know which option you prefer, or if you have a better idea!

.net wpf mvvm coroutines
Posted by: Matt Hamilton
Last revised: 05 Jan, 2011 05:00 AM History
You are looking at revision 1 of this page, which may be out of date. View the latest version.