Windows Workflow Foundation Exceptions

Workflows can use the TryCatch activity to handle exceptions that are raised during the execution of a workflow. These exceptions can be handled or they can be re-thrown using the Rethrow activity. Activities in the Finally section are executed when either the Try section or the Catches section completes. Workflows hosted by a WorkflowApplication instance can also use the OnUnhandledException event handler to handle exceptions that are not handled by a TryCatch activity.

Causes of Exceptions

In a workflow, exceptions can be generated in the following ways:

  • A time-out of transactions in TransactionScope.

  • An explicit exception thrown by the workflow using the Throw activity.

  • A .NET Framework 4.6.1 exception thrown from an activity.

  • An exception thrown from external code, such as libraries, components, or services that are used in the workflow.

Handling Exceptions

If an exception is thrown by an activity and is unhandled, the default behavior is to terminate the workflow instance. If a custom OnUnhandledException handler is present, it can override this default behavior. This handler gives the workflow host author an opportunity to provide the appropriate handling, such as custom logging, aborting the workflow, canceling the workflow, or terminating the workflow. If a workflow raises an exception that is not handled, the OnUnhandledException handler is invoked. There are three possible actions returned from OnUnhandledException which determine the final outcome of the workflow.

  • Cancel - A cancelled workflow instance is a graceful exit of a branch execution. You can model cancellation behavior (for example, by using a CancellationScope activity). The Completed handler is invoked when the cancellation process completes. A cancelled workflow is in the Cancelled state.

  • Terminate - A terminated workflow instance cannot be resumed or restarted. This triggers the Completed event in which you can provide an exception as the reason it was terminated. The Terminated handler is invoked when the termination process completes. A terminated workflow is in the Faulted state.

  • Abort - An aborted workflow instances can be resumed only if it has been configured to be persistent. Without persistence, a workflow cannot be resumed. At the point a workflow is aborted, any work done (in memory) since the last persistence point will be lost. For an aborted workflow, the Aborted handler is invoked using the exception as the reason when the abort process completes. However, unlike Cancelled and Terminated, the Completed handler is not invoked. An aborted workflow is in an Aborted state.

The following example invokes a workflow that throws an exception. The exception is unhandled by the workflow and the OnUnhandledException handler is invoked. The WorkflowApplicationUnhandledExceptionEventArgs are inspected to provide information about the exception, and the workflow is terminated.

Activity wf = new Sequence
{
    Activities =
     {
         new WriteLine
         {
             Text = "Starting the workflow."
         },
         new Throw
        {
            Exception = new InArgument<Exception>((env) =>
                new ApplicationException("Something unexpected happened."))
        },
        new WriteLine
         {
             Text = "Ending the workflow."
         }
     }
};

WorkflowApplication wfApp = new WorkflowApplication(wf);

wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
    // Display the unhandled exception.
    Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
        e.InstanceId, e.UnhandledException.Message);

    Console.WriteLine("ExceptionSource: {0} - {1}",
        e.ExceptionSource.DisplayName, e.ExceptionSourceInstanceId);

    // Instruct the runtime to terminate the workflow.
    return UnhandledExceptionAction.Terminate;

    // Other choices are UnhandledExceptionAction.Abort and
    // UnhandledExceptionAction.Cancel
};

wfApp.Run();

Handling Exceptions with the TryCatch Activity

Handling exceptions inside a workflow is performed with the TryCatch activity. The TryCatch activity has a Catches collection of Catch activities that are each associated with a specific Exception type. If the exception thrown by an activity that is contained in the Try section of a TryCatch activity matches the exception of a Catch<TException> activity in the Catches collection, then the exception is handled. If the exception is explicitly re-thrown or a new exception is thrown then this exception passes up to the parent activity. The following code example shows a TryCatch activity that handles an ApplicationException that is thrown in the Try section by a Throw activity. The exception's message is written to the console by the Catch<TException> activity, and then a message is written to the console in the Finally section.

DelegateInArgument<ApplicationException> ex = new DelegateInArgument<ApplicationException>()
{
    Name = "ex"
};

Activity wf = new TryCatch
{
    Try = new Throw()
    {
        Exception = new InArgument<Exception>((env) =>new ApplicationException("An ApplicationException was thrown."))
    },
    Catches =
    {
        new Catch<ApplicationException>
        {
            Action = new ActivityAction<ApplicationException>
            {
                Argument = ex,
                Handler = new WriteLine()
                {
                    Text = new InArgument<string>((env) => ex.Get(env).Message)
                }
            }
        }
    },
    Finally = new WriteLine()
    {
        Text = "Executing in Finally."
    }
};

The activities in the Finally section are executed when either the Try section or the Catches section successfully completes. The Try section successfully completes if no exceptions are thrown from it, and the Catches section successfully completes if no exceptions are thrown or rethrown from it. If an exception is thrown in the Try section of a TryCatch and is either not handled by a Catch<TException> in the Catches section, or is rethrown from the Catches, the activities in the Finally will not be executed unless the one of the following occurs.

Exception Handling versus Compensation

The difference between exception handling and compensation is that exception handling occurs during the execution of an activity. Compensation occurs after an activity has successfully completed. Exception handling provides an opportunity to clean up after the activity raises the exception, whereas compensation provides a mechanism by which the successfully completed work of a previously completed activity can be undone. For more information, see Compensation.

See also