Note: This blog post is written using the .NET framework 4.0 RC 1

Using Workflow Foundation 4 the NativeActivity is the powerhouse when it comes to building native activities. One of its many capabilities is around error handling. Every so often I run into one of these things where things don’t quite work the way I expect them to and this is one of these cases.

 

The basics of error handling when scheduling child activities.

Whenever a NativeActivity is executed it is passed an instance of the NativeActivityContext which it can use to schedule other activities using the ScheduleActivity() function. This ScheduleActivity() function has a few overloads, one of them using an FaultCallback. This FaultCallback is called when some kind of exception occurs while executing the child activity being scheduled. The fault handling function is called with a couple of parameters including a NativeActivityFaultContext and the exception that is unhandled. The NativeActivityFaultContext contains a HandleFault() function used to indicate that the fault was handled. Not quite as straightforward as a try/catch block but given the asynchronous nature of workflow that would not work.

So I expected the following activity to catch any exceptions and continue.

 

public sealed class MyActivity : NativeActivity
{
    public Activity Body { get; set; }
 
    protected override void Execute(NativeActivityContext context)
    {
        context.ScheduleActivity(Body, FaultHandler);
    }
 
    private void FaultHandler(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
    {
        Console.WriteLine(propagatedException.Message);
        faultContext.HandleFault();
    }
}
 

Do not use, this code has a serious error!

 

Lets test this code by executing the following workflow:

private static Activity CreateWorkflow()
{
    return new Sequence
    {
        Activities =
        {
            new WriteLine { Text = "Start outer sequence." },
            new MyActivity 
            {
                Body = new Sequence
                {
                    Activities = 
                    {
                        new WriteLine { Text = "Start inner sequence." },
                        new Throw { Exception = new InArgument<Exception>(ctx => new DivideByZeroException()) },
                        new WriteLine { Text = "End inner sequence." }
                    }
                }
            },
            new WriteLine { Text = "End outer sequence." }
        }
    };
}

 

Given this workflow I would expect the following output:

 

However what really happens is something else as I receive the following output:

As we can see the second inner WriteLine still executes even though the exception is caught at a higher level!

 

This behavior reminds me of the infamous VB6 On Error Resume Next where an error would just be ignored and the next statement executed. Not really what I was expecting or want.

So the fix is easy. All that is needed is to explicitly cancel the child activity being executed using the CancelChild() function. Below the correct version of my NativeActivity.

public sealed class MyActivity : NativeActivity
{
    public Activity Body { get; set; }
 
    protected override void Execute(NativeActivityContext context)
    {
        context.ScheduleActivity(Body, FaultHandler);
    }
 
    private void FaultHandler(NativeActivityFaultContext faultContext, Exception propagatedException, ActivityInstance propagatedFrom)
    {
        Console.WriteLine(propagatedException.Message);
        faultContext.HandleFault();
        faultContext.CancelChild(propagatedFrom);
    }
}

The correct fault handler

 

Enjoy!

www.TheProblemSolver.nl

Wiki.WindowsWorkflowFoundation.eu