Building Sequential Workflows
Contents
In my last post I showed how to setup a workflow project in Visual Studio and I discussed at a high level the different types of artifacts (Sequential Workflows, State Machine Workflows, and Custom Activities) that can be created with Visual Studio and Workflow Foundation.
In this post I’ll continue where my last post left off by building a Sequential Workflow and discussing it in more detail. The sequential workflow built in this post will be simple and will use the Code Activity and the IfElse Activity. I’ll limit myself to these activities because they are intuitive to use. I’ll also show how to pass parameters to a workflow and how to execute a workflow using the workflow runtime. You can download the code for this post at the link below.
This post is longer than most posts. It also took me longer to get it written to my liking. However, I wanted it to contain enough detail to be usable by a developer new to Visual Studio and Workflow Foundation.
As a quick review, Sequential Workflows represent a workflow style that closely resembles conventional programming techniques. Activities such as IfElse, Code, TransactionScope, CallExternalMethod, and Parallel, to name a few, are drawn directly on the sequential workflow designer to express business logic. For the most part sequential workflows are used in deterministic situations – in other words they are used when the path through the business logic is determined by the data sent into the workflow.
Once you have a workflow project setup as described in my last post you can start adding workflows to it. Right click the project node to get to the project’s context menu. Next select “Add” then select “New Items …” from the context menu. Figure 1 below shows the Add Item Dialog for workflow items that was described in my last post. To get started with a simple sequential workflow, select Sequential Workflow (with code separation) from the list of workflow items shown in Figure 1. Give the new workflow the name “SequentialExpenseReportApproval” and click OK. Figure 2 shows the design view of the newly created sequential workflow.

Figure 1 – Add Item Dialog

Figure 2 – Design view of an empty sequential workflow
There are a few things worth noting in the design view depicted in Figure 2. First, the workflow toolbox is visible and can be found in the left hand side of Figure 2. The Workflow Foundation toolbox displays all the activities that come with the .NET Framework 3.5.
The second thing worth noting in the design view shown in Figure 2 is the project explorer. Notice that when we added a Sequential Workflow with code separation two new files were added to the project. These two new files are shown in the project explorer and are listed below.
· SimpleSequentialWorkflow.Designer.xoml
· SimpleSequentialWorkflow.cs
As the name implies, the first file listed above is used by the designer to keep track of the activities that developers drag onto the workflow designer. This file is an Extensible Application Markup Language (XAML) file. (The WF team decided to give this file an extension of XOML as opposed to XAML because XAML is the extension used for Windows Presentation Foundation user interfaces.) XAML is a designer friendly XML dialect. If you view this file in an XML editor you will see the XML shown in Figure 3.
<SequentialWorkflowActivityx:Class="WorkflowLibrary.SequentialExpenseReportApproval"
x:Name="SequentialExpenseReportApproval"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
SequentialWorkflowActivity>
Figure 3 - WF XAML file
The second file is a “code behind” file. This is a standard code file which contains supporting code needed by a workflow implementation. Figure 4 shows the code in the code behind file. This is where workflow properties are defined and implemented. This file is also where the event handlers for the various workflow activities are implemented. Notice the partial class syntax that is used in the code behind file. This is needed because collectively the XAML file and the code behind file are compiled into a single class. Even though the text in the XAML file is not C# it still contains information that is used by the compiler to produce a class.
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
namespace WorkflowLibrary
{
public partial class SequentialExpenseReportApproval :
SequentialWorkflowActivity
{
}
}
Figure 4 - WF Code Behind file
Are you new to the .NET Framework?
A language enhancement (for both VB.NET and C#) that was introduced in .NET 2.0 is support for partial classes. Partial classes allow your class definition to be split into multiple physical files. During compile time the language’s compiler groups all the partial class files together and compiles them into a single class. Once compiled there is no difference between a class that was compiled from a single file or a class that was compiled from multiple files that used the partial class syntax.
The greatest advantage of partial class files is that they allow a clean separation between designer generated code and code that is implemented by a developer in another file (often called a code behind file). In the case of Workflow Foundation, the designer generated code is the XAML file.
The final observation to make about the design experience provided by Visual Studio is the Properties Dialog which is located in the lower right hand corner of Figure 2. The Properties Dialog is used extensively when developing workflows with Workflow Foundation. Many times workflow activities can be utilized by merely setting properties in the Properties Dialog. This eliminates the need to write code in the underlying code behind file. In Figure 2 the Properties Dialog is showing the properties of the workflow itself. The important property to note here is the Name property of the workflow. Here it is SequentialExpenseReportApproval this is the class name (or type) of the workflow itself. This class name will be used in code to invoke this workflow.
Are you new to Visual Studio?
You can organize the location and layout of Visual Studio’s dialogs any way you wish. The figures in this lesson show the workflow dialogs docked to a side or corner of Visual Studio. However, each of these dialogs can be undocked and displayed in its own Window. You may also move these dialogs around and dock them to the side or corner of Visual Studio that suits your personal preference. Figure 5 shows the visual aids Visual Studio displays while the programming environment is being configured. Simply grabbing one of the dialogs and dragging it will create this view in Visual Studio 2008.
Figure 5 - Dragging Dialogs around Visual Studio 2008
Before I start adding activities to the SequentialExpenseReportApproval, I am going to add a property to the workflow to hold the data that is going to be processed by the workflow. Specifically, I’ll create a property to hold all the information in an Expense Report. To do this we need to create a class that will be used as the type of the workflow property. Figure 6 shows how to add a new class to a Visual Studio project using the New item Dialog. Figure 7 shows the code needed to implement a class named ExpenseReport. This class will be used by the SequentialExpenseReportApproval workflow. It represents a simple expense report that our workflow will approve or reject. Instances of the ExpenseReport class will be processed by instances of the workflow we are going to create in this post. Figure 8 shows the code needed to make a property in our workflow’s code behind file to hold an instance of the ExpenseReport class. Later in this post I will show how an instance of the ExpenseReport class can be passed to a running workflow as a parameter and saved into this property.

Figure 6 – Adding a new class to a Visual Studio Project
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace WorkflowLibrary
{
public class ExpenseReport
{
public decimal Amount;
public string Employee;
public string Title;
}
}
Figure 7 – The ExpenseReport type
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
namespace WorkflowLibrary
{
public partial class SequentialExpenseReportApproval :
SequentialWorkflowActivity
{
// Workflow property - data that will be processed
private ExpenseReport expense;
public ExpenseReport Expense
{
get { return expense; }
set { expense = value; }
}
}
}
Figure 8 – Creating a Workflow property in the code behind file
We are now ready to add our first activity to the workflow. We will start out with a Code activity. The Code activity is the simplest and easiest activity to use. Its purpose is to allow an event handler in the code behind file to be called at the designated location in the workflow.
To add a Code activity to the SequentialExpenseReportApproval workflow, simply drag the Code activity from the WF Toolbox and drop it onto the workflow designer. Once the code activity is dropped onto the workflow designer it will be given a default name of codeActivity1. You can change the activity name by using the properties dialog. If your properties dialog is already visible within Visual Studio as shown in Figure 9 then all you need to do to change the name of the new code activity is to make sure the code activity is selected by clicking on it and then editing the Name property in the properties dialog. We want to change the name to codeStartMessage. Figure 9 shows what the design view of the SequentialExpenseReportApproval workflow should look like once the Code activity has been added and renamed.

Figure 9 – Adding a Code Activity to the Workflow
When the properties dialog is always visible within Visual Studio it always shows the properties of the currently selected object within your workflow – so be sure that you are always looking at the desired object by looking at the Name property before making any edits. Another way to get to this dialog is to right click the new code activity and select Properties from the context menu. The Code activity’s context menu is shown in Figure 10.

Figure 10 - Context menu for the Code Activity
If you look closely at the Code activity in Figure 9 you will notice that it has a warning message icon drawn in the upper right hand side of the activity. This icon indicates that the activity is not fully configured and compiling the workflow at this time will produce compile time errors. You can get more details about the problem by clicking on the warning icon itself. In Figure 9 we see that we are getting a warning because the ExecuteCode property of the codeStartMessage activity has not been specified. This property specifies an event handler that will execute once processing reaches the codeStartMessage activity. The easiest way to setup this event handler is to double click the codeStartMessage activity. Once you do this you will be taken to the code behind file of the workflow and a stub for your event handler will automatically be created for you.
Figure 11 shows the code we will use in our workflow. Here we are merely writing out the values of the Expense property we added in the previous section. In a real world workflow we would want to save this information to some form of persistent storage such as a relational database.
private void codeStartMessage_ExecuteCode(object sender, EventArgs e)
{
Console.Write("Employee: ");
Console.WriteLine(expense.Employee);
Console.Write("Title: ");
Console.WriteLine(expense.Title);
Console.Write("Amount: ");
Console.WriteLine(expense.Amount);
}
Figure 11 – The Code behind for the codeStartMessage Code activity
The IfElse activity works like an If statement in a programming language. An IfElse activity is added to a workflow the same way that a Code activity is added – via drag and drop from the toolbox. For the SequentialExpenseReportApproval we want the IfElse activity to execute after the code activity we added in the previous section. So when you drop it onto the sequential workflow designer be sure to drop it in a location that is located below the codeStartMessage activity. The newly added IfElse activity will have a default name of IfElseActivity1. Change this to IfElseApprovalLogic using the properties dialog.
The IfElse activity is a composite activity. This means that it contains other child activities and will be responsible for their execution at runtime. The only activity that can be added to an IfElse activity is an IfElseBranch activity. Each IfElseBranch added to an IfElse activity represents a possible path through the IfElse activity. When first added to a workflow the IfElse activity contains two IfElseBranch activities. Additional branches can be added by right-clicking the IfElse activity and selecting “Add Branch” from the context menu. The context menu for the IfElse acitivity is shown in Figure 12. We only need two branches so we will not add another branch here. Next, give each branch a meaningful name. Name the first branch IfElseBranchApprove and name the second branch IfElseBranchReject. If you added the IfElse activity in the correct location and named the parent IfElse Activity and its child IfElseBranch activities correctly then the SequentialExpenseReportApproval will look like Figure 13.

Figure 12 - Context menu for the IfElse activity

Figure 13 – SequentialExpenseReportApproval workflow with an IfElse activity
When the runtime engine reaches an IfElse activity it begins evaluating the conditions of the various branches proceeding from the left to right. The condition of each branch is evaluated and the first branch that evaluates to true is run. In Figure 13 you will notice that the warning message is informing us that the first branch of the IfElse activity – IfElseBranchApprove - requires a condition to be specified. There are two ways to specify a condition - through a declarative rule condition or through a code condition. If the last branch is left without a condition then it will serve as our default branch and this branch will execute if all other conditions evaluate to false.
We will use a declarative rule condition for our workflow. To specify this type of condition go to the properties dialog of the IfElseBranchApprove activity and select “Declarative Rule Condition” in the Condition property. This is shown in Figure 14. Once this is done, you will be able to expand the Condition property so that the condition name and the expression can be specified. This is shown in Figure 15. Specify “ApproveRule” as the name for the condition and then click the ellipses next to the Expression property. This will bring up the Workflow Foundation’s Rule Condition Editor to specify the logic of the condition. Figure 16 shows the Rule Condition Editor and the expression used in the IfElseBranchApprove activity. That expression is:
this.expense.Amount < 2000 || this.expense.Title == "Manager"
This rule determines if the expense report will be approved. Here we are saying that if the amount of the expense report is less than $2000.00 or if the submitter is a manager then proceed down the IfElseBranchApprove activity.
Figure 14 - Properties for an IfElseBranch activity
Figure 15 - Properties for an IfElseBranch activity that is using Declarative Rules

Figure 16 - Rule Condition Editor
Notice that this expression uses the Expense property we setup earlier. This is the only expression we will need since we can let our second branch be our default branch and it will contain the reject logic. In other words if the expression above does not evaluate to true then the expense report will be rejected.
We now have the conditions setup for the IfElseBranch activities. Next we need to build the logic that will execute inside each of these branches. It turns out that the IfElseBranch activity is also a composite activity. When an IfElseBranch activity’s condition property evaluates to True then the activities contained within it are executed. You can put just about anything inside an IfElseBranch activity; however, for simplicity we will only put a Code activity in each of our IfElseBranch activities. Drop a code activity inside the IfElseBranchApprove activity and name it codeApproveMessage. Drop another code activity inside the IfElseBranchReject activity and name it codeRejectMessage. Figure 17 and Figure 18 show the code for each activity. Place this code in the workflow’s code behind file. Figure 19 shows the completed workflow. We are now ready to create a host application and run the workflow.
private void codeApproveMessage_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("Expense report approved.");
}
Figure 17 – The Code behind for the codeApproveMessage Code activity
private void codeRejectMessage_ExecuteCode(object sender, EventArgs e)
{
Console.WriteLine("Expense report rejected.");
}
Figure 18 – The Code behind for the codeRejectMessage Code activity

Figure 19 – The completed SequentialExpenseReportApproval workflow
So far we have created our workflow in a project that will be compiled into a DLL. Therefore the next step is to create a host project that can be used to run the workflow. We will create a console application within our WF Sandbox solution and use it as the application that will run the SequentialExpenseReportApproval workflow.
To do this go to the Add Project Dialog by either right clicking the solution node in the project explorer or by clicking File/New/Project … from Visual Studio’s pull down menu. We want to add a console application as shown in Figure 20. So under project types select Windows and under Templates select Console Application. Name the new project ConsoleHost. We now have a project in our solution that will compile to a standalone executable.

Figure 20 – New Console application
By default the new project has minimum references to the assemblies that make up the .NET Framework. It also has no reference to the DLL which will contain our workflow. Therefore we must add several references to the ConsoleHost project. You can add references to a Visual Studio project by using the context menu of a project’s Reference node in the project explorer. This is shown in Figure 21. Select “Add Reference …” to bring up the Add Reference dialog shown in Figure 22.
The .NET tab allows us to create references to .NET Framework assemblies that are needed by the current project. So make sure the .NET tab is selected and add the following references by selecting them and clicking the OK button. This is also shown in Figure 22.
· System.Workflow.Activities
· System.Workflow.ComponentModel
· System.Workflow.Runtime
· System.WorkflowServices
Figure 21 – Context Menu for project references
Figure 22 – Add New Framework Reference
Next, the ConsoleHost project needs a reference to the DLL or assembly which contains the workflow we have been building. To add a reference to a project that is in the same solution use the “Project” tab of the Add Reference dialog. Select the correct project and click the OK button. This is shown in Figure 23. Project references tell the compiler that you are referencing a project within the same solution. This allows the compiler to compile your projects in the correct order.

Figure 23 – Add new Project Reference
We are now ready to run the workflow. Figure 24 shows the code that will run our SequentialExpenseReportApproval workflow. This code needs to get placed in the Program.cs file of the ConsoleHost we just created. There are a few things worth noting in this code. First, notice that to run the workflow you do not directly instantiate it and execute it. The WorkflowRuntime is responsible for creating and executing a workflow. Only the type of the workflow is passed to the WorkflowRuntime object. A single WorkflowRuntime object is responsible for instantiating and managing the lifecycle of all running workflows in a process. Only one workflow is being run by the WorkflowRuntime in the code of Figure 24 but if needed many workflows could be run at the same time. This is often the case when workflows are run in a server environment.
Since running a workflow does not involve direct instantiation of the workflow this means that you do not have a reference to the running workflow. Additionally, there is no way for you to ask the WorkflowRuntime for a reference to a running workflow. This is by design because by default the WorkflowRuntime utilizes the .NET Framework’s thread pool to run workflows. In other words the thread that eventually executes your workflow is not the thread that the WorkflowRuntime is running on. By not allowing external references deadlocks and race conditions are prevented.
When the WorkflowRuntime creates the workflow on your behalf it does give you a reference to a WorkflowInstance object. This allows you to control the workflow (start, abort, terminate, resume, etc.) but you cannot access any of the workflow’s data.
Notice that to pass parameters to the workflow we need to create a Dictionary object and add an instance of our workflow parameter to it. In this case our workflow parameter is an instance of the ExpenseReport class. When adding an instance of the ExpenseReprt class to the dictionary object it is import that it is given the correct key. In the code shown in Figure 24 the key is “Expense”. This must exactly match the workflow property name that will hold the parameter. Remember from Figure 8, where we setup our workflow property, that we named our property “Expense”.
Finally, the CreateWorkflow function takes the type of our workflow and the dictionary of all parameters and creates an instance of the workflow. The CreateWorkflow function returns a WorkflowInstance object which allows us to start execution of the workflow. This is all shown in Figure 24.
When the workflow is run with the parameters that are setup in Figure 24 the output shown in Figure 25 is produced.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using WorkflowLibrary;
namespace ConsoleHost
{
class Program
{
static void Main(string[] args)
{
WorkflowRuntime workflowRuntime = new WorkflowRuntime();
// create and setup the workflow parameter
ExpenseReport expense = new ExpenseReport();
expense.Amount = 2500;
expense.Employee = "Keith Pijanowski";
expense.Title = "Platform Strategy Advisor";
// create the dictionary object to hold the parameter
Dictionary<string, object> parameters = new Dictionary<string, object>();
// this is a key/value pair
parameters.Add("Expense", expense);
// pass the type of the workflow to be created and any parameters
WorkflowInstance instance = workflowRuntime.CreateWorkflow(
typeof(SequentialExpenseReportApproval),
parameters);
instance.Start();
Console.ReadLine();
}
}
}
Figure 24 – Passing parameters and running the workflow
Figure 25 – Output messages
In this post I showed how to build a simple sequential workflow using a Code activity and an IfElse activity. I also showed how to setup a workflow property to hold the data that will be processed by the workflow. In addition to building the workflow I also covered the basics of invoking a workflow. Specifically, I showed how to build a host process, create a workflow instance, pass parameters to a workflow instance, and run a workflow instance.
Future posts will use this same simple workflow to investigate other Activities that are needed for creating real world business processes. I’ll also use this same workflow to show how the workflow runtime manages a workflow’s lifecycle. Understanding the workflow lifecycle is critical to creating reliable and scalable workflows.