Introduction to Windows Workflow Foundation 4.0

This article gives an overview on Windows Workflow Foundation 4.0 using Visual Studio 2010 Beta 2 and some differences with the previous version.

Updates: I added the updated source code that will work with Visual Studio 2010.

Introduction

I was checking Visual Studio 2010 Beta 2 and noticed a lot of differences between
WF3 and WF4. There is no state machine workflow, WCF workflow services are implemented
differently, and there are no more code-beside files, among others. This is done
to improve performance and understandability of workflows.

Note: In this article, WF3 refers to both WF 3.0 and 3.5.

Project Templates

The first noticeable difference is that there are 4 new WF project templates in Visual
Studio, which are shown in Figure 1. It is encouraged to use these new project
types when creating workflows. Of course, the previous project types are still
there when you need it.

Figure 1. WF4 Project Templates

If you might remember, there are two types of workflows in WF3, sequential and state
machine. You can add activities to these workflows. A workflow is different from
an activity. In WF4, a workflow is an activity which contains other activities.
That explains why there is only an Activity Library project template in WF4.

Figure 2. WF 3.5 Project Templates

In .NET 3.5, we can create WCF workflow services but implementing it would most likely
take at least two project types, one for WCF services and one for the WF workflows.
In WF4, we only need the WCF Workflow Service Application project type. Lastly,
there is the Activity Designer Library project template. This lets you create
the design of an activity or how an activity will appear in the WF designer.You can also create an activity designer in WF 3.5 but I think it is quite limited.
Some differences are the association between the activity type and activity designer
is now stored externally in a metadata store rather than specifying it as an
attribute decorating the activity type, and creating an activity designer is
much like creating a control using XAML.

Out of the Box Activities

There are a lot of new types in WF4. These types are now located in the System.Activities.*
namespaces. WF3 types are located in System.Workflow.* namespaces. Again, there
are several noticeable differences. The first thing that caught my attention
was that there was no more state machine workflow type. However, you can create
a workflow with the same functionality by using a Flowchart activity. Another
thing is that there is no more Code activity. That is because there is no more
code-beside file. A workflow is defined using only XAML. We can still use code
though by creating a custom activity. Let’s take a look at the WF4 toolbox to
see other changes.

Figure 3. WF4 Out of the Box Activities

At first glance, we can easily guess what some of these activities do. There are
the activities that correspond to usual programming language constructs like
If, Swith<T>, While and DoWhile. I noticed that exception handling is a
bit different now. There are the TryCatch, Throw and Rethrow activities. In WF3,
exception handling is done by using fault handlers and fault handler activities.
One thing that I don’t like about using fault handlers is that we have to switch
to a different view in the WF designer to see or add fault handlers for a specified
activity. If you are reviewing your workflow, these things are very easy to miss.

There are also collection activities which allow you to add, clear, remove items
or check if an item is in a collection. There are the primitive activities like
Assign, Delay, InvokeMethod, and WriteLine. The Assign activity is like an assignment
statement. The Delay activity, which is also available in WF3, delays execution
for a specified period of time. The InvokeMethod activity lets you execute the
method of an object. There is the WriteLine activity, for easy output. I believe
that these are provided as out of the box activities because there is no code-beside
logic, and the said operations are commonly used. Other things that are new to
me are activities regarding migration and runtime operations. Migration is new
because it is the one that lets a workflow use WF3 activities, by using the Interop
activity. Meanwhile, runtime operations include persisting and terminating a
workflow.

Getting Started with Workflows

To get a better grip of WF4, let’s create a simple workflow that accepts a string
input and returns the reversed string. First, create a project of type workflow
console application. There are 3 files created automatically: the application
configuration file, the file containing the main method, and the workflow file.
Let’s focus on the workflow. First, the workflow should accept a string input.
Open the workflow in the WF designer. You can see three buttons on the lower
left portion of the designer: Variables, Arguments, and Imports. Click on the
Arguments button and add the StringToReverse argument. Let’s also set the argument
as required by setting the IsRequired property in the Properties window. Another
argument we need to add is an out parameter where the reversed string will be
stored. Let’s call this the ReversedString parameter.
Figure 4. Defining the Arguments

Note that WF4 uses Visual Basic expressions. For example, if we want to set the default
value of the StringToReverse argument to null, we have to set the value to Nothing.
Going back, our algorithm for reversing the string is to define a variable and
append the characters of the string starting from the last. The StringBuilder
class will do the job. To use the StringBuilder class, we need to import the
namespace where the class can be found, which is the System.Text namespace. If
you click on the Imports button, you will see that the System.Text namespace
is already included in the list. To define the StringBuilder variable, we first
need to add an activity to our workflow. The activity will serve as the scope
of the variable. For this, let’s create a Sequence activity by dragging it from
the toolbox to the designer. We need to add a Sequence activity since we will
add a sequence of activities to it later on. Now let’s add While and Assign activities.
We will use the While activity to iterate over the characters of the input string.
Meanwhile, the Assign activity will be used to assign the out parameter to the
reversed string.
Figure 5. Defining the Variables

Select the Sequence activity, click on the Variables button and add the StringBuilder
variable like in Figure 5. To specify the variable type, you must know the assembly
where the type is located. Let’s also create a counter variable in the While
activity that we will use as the index in iterating over the characters of the
string, starting from the last. Now, set the condition to Counter > 0. After
this, add another Sequence activity, this time inside the body of the While activity.
We’ll be adding two activities to it, one for decrementing the counter variable
and one for appending a character to the StringBuilder object. The first one
will be done by using an Assign activity while the second requires an InvokeMethod
activity, which is used in calling the Append method of the StringBuilder object.

Figure 6. While Activity’s Body

To use an InvokeMethod activity, you should specify the values of either TargetType
or TargetObject, and the MethodName properties. The TargetType and TargetObject
properties cannot be set at the same time. You should set the TargetType if you
want to call a static method. Otherwise, set the TargetObject to call an instance
method. Now, set the MethodName to Append. To set the parameters of the method,
we need to set the Parameters property in the Properties window in Visual Studio.

Figure 7. InvokeMethod Activity Properties

Here, you can see all the properties for the activity currently selected. Now, set
the parameters by clicking on the button with the ellipsis. A dialog will be
shown where the parameters can be added. You can specify the direction, type
of the parameter and the value. In our example, the value is set to a character
in the StringToReverse string, obtained using the Counter variable as the index.

Figure 8. Setting Method Parameters

All that is left is to return the reversed string. As said earlier, we will use an
Assign activity to set the out parameter to the reversed string. Just set the
To propery to the ReversedString out parameter and the Value propery to the ToString
method of the ReverseStringBuilder object. The code showing the main program
is shown below.

In our example, we used the WorkflowInvoker class’ Invoke method to execute the workflow.
The Invoke method is an overloaded method. The one we used here can accept workflow
parameters by specifying them inside an IDictionary<string, object> object.
The results are also returned using an IDictionary<string, object> object.
It is important to remember the names of the workflow arguments since these will
be used for setting or getting their values. There’s an overload where you can
specify the return value by using generics but I can’t find a way on how to do
it using the WF designer. I guess it could be done by using code.

Figure 9. The Workflow Console Application

Create Workflow Using Code

Developers will most likely use the WF designer and XAML to create workflows. However,
it is possible to create a workflow using code only. Let’s try to convert the
previous workflow example into code. First, create a class that derives from
the abstract Activity class and override the Implementation property. The Implementation
property is a delegate that encapsulates a method which returns an object of
type Activity. Let’s leave out the implementation first and define the workflow
arguments. Arguments are defined as properties of the class using the InArgument,
OutArgument, and InOutArgument types.

Implementing the activity is actually straightforward as you can see in the code
below. The Sequence class has properties used for defining the display name,
variables, and code activities. Other activities are pretty much the same. You
just need specify constructor parameters or set its properties. I guess the only thing that needs some explanation is how to get or set variables.
Getting the values of the arguments or variables require an ActivityContext if
we are using the Get or Set methods. In a custom code activity, we have access
to the CodeActivityContext in the overridden Execute method. However, in the
case of our example, there is no ActivityContext object, although I haven’t explored
if it is really inaccessible from the Implementation property. In our example,
we will use the VisualBasicValue<T> and VisualBasicReference<T> types
to access the variables.

According to MSDN Library, VisualBasicValue<T> contains an expression that
evaluates to an r-value and supports binding of In arguments while VisualBasicReference<T>
contains an expression that evaluates to an l-value and supports binding of Out
arguments. You can see in the sample code that we use VisualBasicValue<T>
as parameter to the InArgument<T> constructor and the VisualBasicReferenceValue<T>
as parameter to the OutArgument<T> constructor. If we are not going to
use the variables as values of In or Out arguments and is interested only in
getting the value, then we only use VisualBasicValue<T>.

Workflow Services

Let’s try now to create a WCF workflow service that also reverses a string. Create
a project of type WCF Workflow Service Application. This automatically creates
a workflow with a ReceiveAndSendReply activity, which is actually a Sequence
containing Receive and Send activities. The Receive activity corresponds to the
Receive activity in WF3 but there are several differences. To create a workflow
service using a Receive activity in WF3, you need to create an interface which
defines the service contract and the Receive activity implements an operation
of that interface. Meanwhile, creating a Receive activity in WF4 means that you
are defining the operation contract. You can specify the service contract by
setting the ServiceContractName property of the Receive activity. There are other
properties like CanCreateInstance to specify if a Receive activity is the starting
point of the workflow and SerializerOption to specify whether to use DataContractSerializer
or XMLSerializer.

Figure 10. ReceiveAndSendRelpy Activity

Going back to our example, I’ll be defining a DataContract that will contain the
string to be reversed and the reversed string. Using a DataContract is not necessary
in our example but I just want to illustrate how to use a DataContract here.
Build the application and change the data type of the variable data to ReverseStringServiceData.
I’ve had some trouble doing this because a message box appears saying that some
object is null. I ended up changing the code using the XML editor.

using System.Runtime.Serialization;

[DataContract]

publicclassReverseStringServiceData

{

[DataMember]

publicstring StringToReverse { get; set; }

[DataMember]

publicstring ReversedString { get; set; }

}

Listing 3. The DataContract

After this, click on the Content property and choose the Parameters radio button.
The Message radio button should be chosen if you opt to use a MessageContract.
Add a new parameter and set the name to ReverseStringServiceDataInput, the type
to ReverseStringServiceData, and the variable to data. Since we already have
a workflow for reversing the string, let’s just reuse it. I copied the code-only
workflow to the service project, and it is automatically added to the WF4 toolbox.
Drag the activity and set the arguments StringToReverse to data.StringToReverse
and ReversedString to data.ReversedString. Now we only need to specify the parameters
in the Send activity. This time, set the name to ReverseStringServiceDataOutput,
the type to ReverseStringServiceData, and the value to data. Let’s run the application
using the debugger. This will launch the ASP .NET Development Server and the
WCF Test Client. Using the WCF Test Client, we can now test our service without
creating our own client application.
Figure 11. Testing the Workflow Service

The Flowchart

The last thing that I would like to discuss is the Flowchart activity since it replaces
the State Machine workflow, which is a big change. To illustrate how to use the
Flowchart activity, let’s create a simple State Machine workflow that we will
try to convert into a Flowchart activity. This workflow is shown in Figure 12.
The workflow includes 4 State activities (Waiting, Submitted, ApprovalPending,
and Completed) and 5 EventDriven activities (SubmitOrder, UpdateOrder, SubmitOrderToManager,
ApproveOrder, and RejectOrder).

Figure 12. Ordering State Machine Workflow

First, we have to create a bookmark for each EventDriven activity. It is different
if we are creating a WCF Workflow Service, because you need to use Receive activities
instead of bookmarks. A bookmark allows you to resume the execution of a workflow
where the bookmark is, thus the name. Creating a bookmark is just creating a
class that derives from the NativeActivity class. To better understand, check
the SubmitOrder class definition below.

publicclassSubmitOrder : NativeActivity<Order>

{

protectedoverridebool CanInduceIdle

{

get

{

returntrue;

}

}

protectedoverridevoid Execute(NativeActivityContext context)

{

context.CreateBookmark("SubmitOrder", BookmarkResumed);

}

privatevoid BookmarkResumed(NativeActivityContext context,

Bookmark bk, object state)

{

Result.Set(context, state);

}

}

Listing 4. SubmitOrder Bookmark

It is quite easy to define a bookmark. Just derive from the NativeActivity class
and override the Execute method. If the activity should return something, then
derive from the NativeActivity<T> class, just like in the SubmitOrder example.
Now, we can create the bookmark by calling the CreateBookmark method of the context.
You can specify the callback method to execute when the execution is resumed.
In our example, the Result is set to the Order object. We also need to override
the CanInduceIdle property and return the Boolean value true. This causes our
workflow to become idle, which is what we want. I’ve also created other bookmarks
like UpdateOrder, SubmitOrderToManager, ApproveOrder, and RejectOrder although
it is not necessary to create all of them. It is possible to just reuse a bookmark.

After building our project, the activities are added to the WF4 toolbox. We can now
use them in our Flowchart activity. Figure 13 shows the equivalent Flowchart
activity to the State Machine workflow shown earlier. I’ve put up WriteLine activities
to see the flow of the program later on. If we think of this workflow as a state
machine, the Waiting state corresponds to the SubmitOrder and WriteLine activities.
The Submitted state is the UpdateOrSubmit Pick activity and the FlowSwitch activity
connected to it. The same goes for the ApprovalPending state, as you can see
in the figure.

Figure 13. Flowchart Activity

Let’s take a look at the UpdateOrSubmit Pick activity. The Pick activity lets us
add PickBranch activities, wherein a PickBranch activity is executed depending
on some trigger. Since there are 2 EventDriven activities in the Submitted state, there are also 2
PickBranch activities in the Pick activity. We then set the Trigger property
of each PickBranch to a bookmark. Once workflow execution is resumed through
a bookmark, the corresponding Action is executed. One thing that I haven’t gone
through yet is that I created a string variable called nextState. This is used
by the FlowSwitch activity to determine what activity is going to execute next.

Figure 14. Pick Activity

The following listing shows how to main program. You can see that the ResumeBookmark
method is used to resume the execution of the workflow.