Inside Workflow Foundation Activities

Activities are the heart of WF. Together they form workflows, control the flow or do loops.

They can either be simple and atomic or composite.

Simple activities can do one thing as calling a database or web service, terminating the workflow or something else.

Composite activities consist of other activities - they have children within. If the "parent" composite activity is executed, it controls which children are executed in which order. The simplest composite activity, the sequence executes all it's children in a sequential way. A parallel activity consists of several sequences executes at the same time. But there are even more complex composite activities like IF statements, or loops. Even the entire workflow (either sequential or state machine based) is nothing more than a special activity with custom logic and additional properties.

Regardless of whether it is a simple or composite activity, each activity (at least somewhere above in the inheritance tree) inherits of Activity class.

 public class MyActivity : Activity
{
    protected override void Initialize(IServiceProvider provider);

    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext);
    protected override ActivityExecutionStatus Cancel(ActivityExecutionContext executionContext);
    protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext,
                                                                Exception exception);
    protected override void Uninitialize(IServiceProvider provider);
}

What you get, is basic virtual methods, which you can override to provide the behaviour of your activity for this state.

Speaking of states - each activity implements a finite state machine. That means the activity is guaranteed to be in one of the following states:image State transitions may occur only in the directions of the arrows. White arrows are runtime-, yellow ones developer initiated.

This activity automaton displays the entire lifecycle of an activity:

  • At creation of the workflow Initialize() is called on all activities. They are afterwards placed in the Initialized state.
  • The activity is placed into Executing state and it's Executing() method is called. Within it the activity's actual work is done.
  • If an error occurs, the activity automatically transitions into Faulting, and HandleFault() is called.
  • Executing activities can be canceled by other activities to stop their execution.
  • Finally if all the execution, cancellation or fault handling work is done, the activity is closed by the developer.
  • Additionally there is the possibility to "undo" already executed activities. For that situation you can implement the ICompensatableActivity interface, which defines a Compensate() method. This method is called in the Compensating state and allows you to provide custom undo logic.

So to write you custom little activity, just inherit of the Activity base class and override the suitable methods for the states you want to provider logic for. To pass data from outside into the activity you can use DependencyProperties, a new concept in .NET 3.0 which are also used with WPF.

Here is a sample of a ConsoleWritelineActivity:

 public class ConsoleWriteline : Activity
{
    public static DependencyProperty OutputProperty = DependencyProperty.Register("Output",
        typeof(string), typeof(ConsoleWriteline));

    public string Output
    {
        get
        {
            return ((string)(base.GetValue(ConsoleWriteline.OutputProperty)));
        }
        set
        {
            base.SetValue(ConsoleWriteline.OutputProperty, value);
        }
    }
    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
    {
        Console.WriteLine(this.Output);
        return ActivityExecutionStatus.Closed;
    }
}