Handling Errors in Caliburn.Micro’s IResult – Part I

The Problem

One of Caliburn.Micro’s nicest feature is, hands down, the concept of Actions. In that concept the IResult plays an important role, especially when using Coroutines. If you don’t know about them, you should definately read up on them here first.

So, let’s assume we are executing a Coroutine which first shows a loading screen to the user, then starts processing a lot of data and finally hides the loading screen once the processing is finished. Since the processing is also likely to fail for whatever reason we want to handle the error by executing a Rescue Coroutine.

public IEnumerable ProcessData()
{
	yield return new BusyResult("Processing...");

	yield return new ProcessDataResult();

	yield return new NotBusyResult();
}

The implementation of those results is irrelevant since we want to have a look at how we can handle the error during the processing in a nice (reusable) way.

The First Attempt

Well, let’s see how we can handle it at all. The first approach is to ignore the built-in mechanism and expose an error property on the result which will then be checked during the execution of the Coroutine.

public IEnumerable ProcessData()
{
	yield return new BusyResult("Processing...");

	var processDataResult = new ProcessDataResult();
	yield return processDataResult;

	if (processDataResult.Error != null)
	{
		// We could use Coroutine.BeginExecute(Rescue().GetEnumerator()); but than the context would be null
		foreach (var rescueResult in Rescue())
		{
			yield return rescueResult;
		}
		yield break;
	}
	else
	{
		yield return new NotBusyResult();
	}
}

public IEnumerable<IResult> Rescue()
{
	yield return new NotBusyResult();

	// more rescue stuff
}

public class ProcessDataResult : IResult
{
	public Exception Error { get; private set; }

	public void Execute(ActionExecutionContext context)
	{
		try
		{
			//process the data
		}
		catch (Exception e)
		{
			Error = e;
		}

		Completed(this, new ResultCompletionEventArgs());
	}

	public event EventHandler<ResultCompletionEventArgs> Completed;
}

Although this works, there are some problems with this approach.

  • We need to add an error property to each result where an error is likely (which may not be always possible)
  • The syntax for executing the Rescue Coroutine is quite ugly and not easy to comprehend
  • The method itself gets exponentially more complex for every result which can fail
  • Calling different rescues for different error is tideous

The Better Attempt

The second approach uses the built-in mechanism by raising the Completed Event with the Error roperty of the ResultCompletitionEventArgs set to the actual error.

public IEnumerable<IResult> ProcessData()
{
	yield return new BusyResult("Processing...");

	var processDataResult = new ProcessDataResult();
	processDataResult.Completed += (sender, args) =>
									   {
										   if (args.Error != null)
											   Coroutine.BeginExecute(Rescue().GetEnumerator());
									   };

	yield return processDataResult;

	yield return new NotBusyResult();
}

public IEnumerable<IResult> Rescue()
{
	yield return new NotBusyResult();

	// more rescue stuff
}

public class ProcessDataResult : IResult
{
	public void Execute(ActionExecutionContext context)
	{
		try
		{
			//process the data
		}
		catch (Exception e)
		{
			Completed(this, new ResultCompletionEventArgs { Error = e });
		}

		Completed(this, new ResultCompletionEventArgs());
	}

	public event EventHandler<ResultCompletionEventArgs> Completed;
}

With this approach we don’t need to add an extra property to our Result which is a huge gain but there are still some disadvantages.

  • We ‘lose’ the context in the Rescue Coroutine
  • Syntax still not optimal
  • Calling different rescues is still tedious

The Final Solution

So, what we want is basically

  • a nice syntax,
  • something that works for every implementation of IResult,
  • execute the Rescue Coroutine with the same context as the failing Coroutine,
  • and a way to handle different types of errors

Whew, that’s quite a bit to ask for. Let’s have a look at the first two points. Since we want it to work on every implementation of IResult, we cannot use inheritance but how about an Extension Method for IResults together with some fluent syntax?

public IEnumerable<IResult> ProcessData()
{
	yield return new BusyResult("Processing...");

	yield return new ProcessDataResult()
		.Rescue<IOException>().With(coroutine: IORescue)
		.Rescue().With(coroutine: GeneralRescue);

	yield return new NotBusyResult();
}

public IEnumerable<IResult> IORescue(IOException exception)
{
	yield return new NotBusyResult();

	// more rescue stuff
}

public IEnumerable<IResult> GeneralRescue(Exception exception)
{
	yield return new NotBusyResult();

	// more rescue stuff
}

That doesn’t look too bad, does it?

But since this post got really long, I will show the implementation of the solution in the next part. So you either wait for the next post or you can have a look at the code yourself

Category(s): C#, Caliburn.Micro, CMContrib
  • http://www.planetgeek.ch Daniel Marbach

    Hy
    Posted a similar approach I did in the caliburn forum. See here:

    http://caliburn.codeplex.com/discussions/252832

    Thanks for sharing!

    Daniel

  • http://www.planetgeek.ch Daniel Marbach

    I looked into the code. Do you build up the inner result on the IoC container? I didn’t see it in the code. In my implementation I use the SequentialResult internally which automatically builds up the result.

    Daniel

  • http://www.codesomnia.de Kevin

    BuildUp of the inner result by the IoC container is currently not supported but I will add that for the second part. I actually forgot about the build up completely :/ Thanks for the hint

  • http://www.planetgeek.ch Daniel Marbach


    public void Execute(ResultExecutionContext context)
    {
    this.context = context;

    var sequentialResult = new SequentialResult(new List { this.result });
    sequentialResult.Completed += this.HandleCompleted;
    sequentialResult.Execute(context);
    }

    private void HandleCompleted(object sender, ResultCompletionEventArgs e)
    {
    if (e.Error != null)
    {
    var exception = e.Error;

    var rescueResults = this.handler(exception);

    var sequentialResult = new SequentialResult(rescueResults);
    sequentialResult.Execute(this.context);
    e.Error = null;
    e.WasCancelled = true;
    }

    this.Invoke(() => this.Completed(sender, e));
    }

    Here for example my catching extension which uses the above code internally:


    public static IResult Catch(this IResult result, Func<Exception, IEnumerable> handler)
    {
    return new CatchingResult(result, handler);
    }

    public static IResult Catch(this IResult result, Func handler)
    {
    return new CatchingResult(result, exception => new List { handler(exception) });
    }
    public static IResult Catch(this IResult result, Action handler)
    {
    return new CatchingResult(result, exception => { handler(exception); return new List { new EmptyResult() }; });
    }

  • http://www.codesomnia.de Kevin

    yeah thats’s quite similar to what i did. I think i read your post on the caliburn forum some time ago but it lacked the implementation so i did it myself. funny that it turns out quite similar :)