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.

class Program

{

static void Main(string[] args)

{

Console.Write("Enter string: ");

string stringToReverse = Console.ReadLine();

string reversedString = string.Empty;

IDictionary<string, object> results = WorkflowInvoker.Invoke

(

new ReverseStringWorkflow(),

new Dictionary<string, object>{ {"StringToReverse", stringToReverse } }

);

Console.Write("Reversed string: ");

Console.WriteLine(results["ReversedString"]);

Console.ReadKey();

}

}

Listing 1. Main Program

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.

public class ReverseStringWorkflow : Activity

{

public InArgument<string> StringToReverse { get; set; }

public OutArgument<string> ReversedString { get; set; }


protected
override Func<Activity> Implementation

{

get

{

return () =>

{

Sequence sequence = new Sequence

{

Variables =

{

new Variable<StringBuilder>("ReverseStringBuilder", new StringBuilder())

},

Activities =

{

new While

{

Variables =

{

new Variable<int>("Counter")

{

Default = new VisualBasicValue<int>("StringToReverse.Length")

}

},

Condition = new VisualBasicValue<bool>("Counter > 0"),

Body = new Sequence

{

Activities =

{

new Assign

{

To = new OutArgument<int>(new VisualBasicReference<int>("Counter")),

Value = new InArgument<int>(new VisualBasicValue<int>("Counter - 1"))

},

new InvokeMethod

{

MethodName = "Append",

TargetObject = new InArgument<StringBuilder>(new VisualBasicValue<StringBuilder>("ReverseStringBuilder")),

Parameters =

{

new InArgument<char>(new VisualBasicValue<char>("StringToReverse.Chars(Counter)"))

}

}

}

}

},

new Assign

{

To = new OutArgument<string>(new VisualBasicReference<string>("ReversedString")),

Value = new InArgument<string>(new VisualBasicValue<string>("ReverseStringBuilder.ToString()"))

}

}

};


return
sequence;

};

}

set

{

base.Implementation = value;

}

}

}

Listing 2. Workflow using Code

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]

public class ReverseStringServiceData

{

[DataMember]

public string StringToReverse { get; set; }


[DataMember]

public string 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.

public class SubmitOrder : NativeActivity<Order>

{

protected override bool CanInduceIdle

{

get

{

return true;

}

}

protected override void Execute(NativeActivityContext context)

{

context.CreateBookmark("SubmitOrder", BookmarkResumed);

}


private
void 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.

class Program

{

static void Main(string[] args)

{

WorkflowApplication workflowApp = new WorkflowApplication(new OrderingWorkflow());

workflowApp.Run();


Order
order = new Order { Id = 1 };

workflowApp.ResumeBookmark("SubmitOrder", order);
order.Id = 2;

workflowApp.ResumeBookmark("UpdateOrder", order);

workflowApp.ResumeBookmark("SubmitOrderToManager", null);

workflowApp.ResumeBookmark("RejectOrder", null);

workflowApp.ResumeBookmark("SubmitOrderToManager", null);

workflowApp.ResumeBookmark("ApproveOrder", null);

Console.ReadLine();

}

}

Listing 5. Executing the Flowchart

The result of the program is shown in the following figure.



Figure 15. Output of the Ordering Workflow

You can download Visual Studio 2010 Beta 2 solution here, which contains all the example projects used in this article.

Download the Visual Studio 2010 solution here.

By Michael Detras   Popularity  (20542 Views)
Biography - Michael Detras
.NET developer. Interested in WPF, Silverlight, and XNA.
My blog