Windows Workflow Tutorial: Using Basic Flow Control (IfElse, While, Parallel) for Windows Workflow Foundation

Rob Green
MCW Technologies LLC

Articles in this series

Download the code for this article

Introduction

As you have seen in the previous tutorials in this series, workflows model business processes. As you build a workflow, you are answering several questions:

  • What happens?
  • How does it happen?
  • When does it happen?

Activities and the custom code associated with them are the “what” and “how” of a workflow. To address the “when” of a workflow, the order the activities appear in a sequential workflow and the transitions between states in a state machine workflow are one part, and the activities that control the flow of the workflow are the other. Windows Workflow Foundation (WF) provides a number of flow control activities, and you’ll spend the next few tutorials in this series exploring how to use them.

In this tutorial, you will investigate the three workflow flow control basic building blocks: the IfElse, While and Parallel activities. IfElse provides branching. While provides repeated execution. Parallel provides a “round-robin” execution of multiple child activities.

Introducing the IfElse Activity

The IfElse activity is the most basic of the flow control activities – it enables you to control whether one or more activities executes. As an example, in an inventory-checking system, you might want one set of activities to execute if an item is in stock and a different set of activities to execute if the item is not in stock – the IfElse activity enables you to do that.

The IfElse activity contains one or more IfElseBranch activities. At runtime, the workflow evaluates the Condition property of the first branch. If the condition evaluates to true, the activities in that branch execute. If the condition evaluates to false, the workflow skips that branch and evaluates the condition of the next branch, if there is one.

Each branch except the last branch must have a condition. If the last branch has a condition, the activities in it will execute if that condition evaluates to true. If it does not have a condition, the activities in it will execute if none of the other branches has a condition that evaluates to true. In this scenario, the last branch is the Else branch.

An IfElse activity can have as many branches as you need. You can also nest IfElse activities. Each branch can contain as many activities as you need. To return a true or false value for the condition property, you can use a declarative rule condition, which is a simple expression, or a code condition, which is a method that is evaluated at runtime.

Create a Workflow Project

To demonstrate the first two flow control activities in this tutorial, you will create a sequential workflow project to model checking the inventory for an item and if necessary restocking the item.

To get started, in Visual Studio 2008 select File | New | Project to display the New Project dialog box. In the list of project types, select Workflow, displaying the list of templates. For this demonstration, select the Sequential Workflow Console Application template, name your application IfElseWhileDemo, and select an appropriate folder for the project. Click OK to create the project.

Next, you will create an XML file containing inventory data for three products; you’ll be using this XML file instead of hard-coding the values in this sample project. To create the file, select Project | Add New Item, and select the XML File template. Name the file Inventory.xml and click Add. Add the following XML:

<Products>
  <Product>
    <ProductID>1</ProductID>
    <OnHand>100</OnHand>
    <Available>50</Available>
  </Product>
  <Product>
    <ProductID>2</ProductID>
    <OnHand>10</OnHand>
    <Available>250</Available>
  </Product>
  <Product>
    <ProductID>3</ProductID>
    <OnHand>25</OnHand>
    <Available>50</Available>
  </Product>
</Products>

In the Solution Explorer, select Inventory.xml. Set the Copy to Output Directory property to Copy always.

Now you will create the workflow. Return to the workflow designer by selecting the Workflow1 tab. You can also double click Workflow1 in the Solution Explorer.

Select View | Code. The console application that you will create will pass the ID of the product for which you want to check inventory. To enable the workflow to accept this value, add the following property to the workflow:

Private productIDValue As Integer
Public Property ProductID() As Integer
  Get
    Return productIDValue
  End Get
  Set(ByVal value As Integer)
    productIDValue = value
  End Set
End Property
public int ProductID { get; set; }

Now add the following declarations to store the amount on hand and the amount on order for the specific product:

Public onHand As Integer = 0
Public available As Integer = 0
public int onHand = 0;
public int available = 0;

Add Activities to the Workflow

Select View | Designer to return to the workflow designer. Drag a Code activity From the Toolbox onto the workflow. Name this activity LookupProduct. Double-click LookupProduct and add the following code to the activity’s ExecuteCode event handler:

Dim xmlFile = XDocument.Load(IO.Path.Combine( _
  AppDomain.CurrentDomain.BaseDirectory, "Inventory.xml"))
Dim inventory = _
  From product In xmlFile.Descendants("Product") _
  Where Convert.ToInt32( _
    product.Element("ProductID").Value) = ProductID _
  Select _
    OnHand = Convert.ToInt32(product.Element("OnHand").Value), _
    Available = Convert.ToInt32( _
      product.Element("Available").Value)
onHand = inventory.First.OnHand
available = inventory.First. Available
var xmlFile = System.Xml.Linq.XDocument.Load(
  System.IO.Path.Combine(
  AppDomain.CurrentDomain.BaseDirectory, "Inventory.xml"));
var inventory =
  from product in xmlFile.Descendants("Product")
  where Convert.ToInt32(
  product.Element("ProductID").Value) == ProductID
  select new
  {
    OnHand = Convert.ToInt32(product.Element("OnHand").Value),
    Available = Convert.ToInt32(
      product.Element("Available").Value)
  };
onHand = inventory.First().OnHand;
available = inventory.First(). Available;

This code uses a LINQ To XML query to retrieve the OnHand and Available elements for the specified product. These are stored in the onHand and available fields.

Select View | Designer to return to the workflow designer. You will instruct the workflow to take one action if there is sufficient quantity of the item in stock and another action if there is not.

Drag an IfElse activity from the Toolbox onto the workflow below LookupProduct. Name this activity CheckOnHand. Rename ifElseBranchActivity1 to IfSufficientOnHand. Rename ifElseBranchActivity2 to IfNotSufficientOnHand.

Conditions: Using a Declarative Rule Condition

At this point, the IfSufficientOnHand activity indicates an error. You haven’t told it yet what condition to evaluate. To do that, select IfSufficientOnHand. In the Properties window, find the Condition property, select the drop-down arrow to the right of the property’s value, and select Declarative Rule Condition (see Figure 1).

For the Condition property, you have the option of either defining a rule in the Properties window (Declarative Rule Condition) or creating a condition in code (Code Condition). For this activity, you are selecting the Declarative Rule condition and defining the condition in the Properties window; you’ll visit the other option in our next IfElse activity.

Figure 1. Choose the type of condition you want to use.

Click the “+” sign to the left of the Condition property, expanding the property. Set the ConditionName property to SufficientOnHand. The condition will get a default name if you don’t specify one, but naming the condition makes it easier for you to to identify the condition later on, as well as use it in a different place within your workflow. Click the Expression property and then click the ellipsis to the right of that property. Enter the condition shown in Figure 2. Click OK to close the editor.

Figure 2. Create the IfElseBranch activity’s rule condition.

Using the WF Rule Condition Editor

The Rule Condition Editor is essentially a code editor. You can enter expressions into it just as you would if you were using expressions in your code. You get the benefit of IntelliSense, just as you do in the Visual Studio code editor (see Figure 3).

Figure 3. Use IntelliSense to help you create expressions.

The Rule Condition Editor supports the following operators:

  • Relational: ==, =, !=
  • Comparison: <, <=, >, >=
  • Arithmetic: +, - , *, /, MOD
  • Logical: AND, &&, OR, ||, NOT, !
  • Bitwise: &, |

Notice there is a mix of Visual Basic and C# syntax in this list. You can enter expressions using either language. However, be aware that Visual Studio uses the Visual C# compiler to validate expressions. The editor will convert simple Visual Basic syntax to C# prior to compiling. For example, you could enter me.onHand in the Rule Condition Editor. When you click OK, the editor with convert that to this.onHand.

If you are a Visual Basic developer, you are used to case insensitivity (if you enter me.onhand in the code editor, Visual Studio will automatically convert that to Me.onHand). It is important to note that the Rule Condition Editor is case sensitive and, like the Visual C# code editor, will enforce case sensitivity. This means that if you enter me.onhand in the Rule Condition Editor and click OK, you will see the error message shown in Figure 4.

Figure 4. The Rule Condition Editor is case sensitive.

Finish the Workflow

From the Toolbox, drag a Code activity into IfSufficientOnHand. Name the activity ReportOnHand. Double-click ReportOnHand and add the following code to the activity’s ExecuteCode event handler:

Console.WriteLine("There are {0} units of product {1} on hand" & _
  vbCrLf, onHand, ProductID)
Console.WriteLine("There are {0} units of product {1} on hand\n" ,
  onHand, ProductID);

Select View | Designer to return to the workflow designer.

From the Toolbox, drag a Code activity into IfNotSufficientOnHand. Name the activity Reorder. Double-click Reorder and add the following code to the activity’s ExecuteCode event handler:

Console.WriteLine( _
  "There are only {0} units of product {1} on hand", _
  onHand, ProductID)
Console.WriteLine("It is time to reorder" & vbCrLf)
Console.WriteLine(
  "There are only {0} units of product {1} on hand" ,
  onHand, ProductID);
Console.WriteLine("It is time to reorder\n");

Select View | Designer to return to the workflow designer. The workflow should look like

Figure 5. The workflow should look like this.

Calling the Workflow from the Console Application

Your final step will be to modify the console host application to pass the id of a product to the workflow. In the Solution Explorer, double click Module1.vb or Program.cs. Add the following code to the Main method, replacing the existing line of code that calls the CreateWorkflow method:

Dim parameters As New Dictionary(Of String, Object)
parameters.Add("ProductID", 1)
workflowInstance = workflowRuntime.CreateWorkflow( _
  GetType(Workflow1), parameters)
var parameters = new Dictionary<string, object>();
parameters.Add("ProductID", 1);
WorkflowInstance instance = workflowRuntime.CreateWorkflow(
  typeof(IfElseWhileDemo.Workflow1), parameters);

Save and press Ctrl + F5 to run your project. You should first see the following output:

There are 100 units of product 1 on hand
Press any key to continue . . .
Press any key to exit the application.

To see what happens if there is insufficient stock for a product, make the following modification to the Main method:

parameters.Add("ProductID", 2)
parameters.Add("ProductID", 2);

Save and press Ctrl + F5 to run your project. You should see the following output:

There are only 10 units of product 2 on hand
It is time to reorder
Press any key to continue . . .
Press any key to exit the application.

Modify the Workflow to Add a Nested IfElse Activity

Return to the workflow designer in Visual Studio. You will now add additional flow control logic to handle inventory reordering - instruct the workflow to perform an action if you can successfully reorder the product and a different action if you cannot.

Drag an IfElse activity from the Toolbox into IfNotSufficientOnHand below Reorder. Name this activity CheckReorderStatus. Rename ifElseBranchActivity1 to IfReordered. Rename ifElseBranchActivity2 to IfNotReordered. The workflow should look like Figure 6.

Figure 6. The workflow should look like this.

Conditions: Using a Code Condition

Select IfReordered. In the Properties window, find the Condition property, select the drop-down arrow to the right of the property’s value, and select Code Condition (see Figure 7).

Figure 7. Choose the type of condition you want to use.

Click the “+” sign to the left of the Condition property, expanding the property. Set the Condition property to PlaceReorder. Press Enter. Visual Studio creates the PlaceReorder method and opens the code editor. The new method will look like the below:

Private Sub PlaceReorder(ByVal sender As System.Object, _
  ByVal e As System.Workflow.Activities.ConditionalEventArgs)
End Sub
private void PlaceReorder(object sender, ConditionalEventArgs e)
{
}

The PlaceReorder method is actually an event handler. When you specify Code Condition, Visual Studio creates an instance of the CodeCondition class and adds it to the workflow. The CodeCondition class has an Evaluate method. The workflow runtime calls this method when it needs to evaluate the condition. This method causes the Condition event to occur. The PlaceReorder method handles this event.

The second argument in this method is of the type ConditionEventArgs. This class has a Result property, which is a Boolean value. Setting this property to true or false will cause the condition to evaluate to true or false.

Add the following code to the PlaceReorder method:

e.Result = onHand + available >= 100
e.Result = onHand + available >= 100;

For the purposes of this tutorial, a reorder is successful if there is enough of the product available to have at least 100 units in stock.

Finishing the IfElse Workflow

Select View | Designer to return to the workflow designer. From the Toolbox, drag a Code activity into IfReordered. Name the activity ReportReorder. Double-click ReportReorder and add the following code to the activity’s ExecuteCode event handler:

Console.WriteLine("{0} units of product {1} will be ordered" & _
  vbCrLf, 100 – onHand, ProductID)
Console.WriteLine("{0} units of product {1} will be ordered\n" ,
  100 – onHand, ProductID);

Select View | Designer to return to the workflow designer.

From the Toolbox, drag a Code activity into IfNotReordered. Name the activity ReportFailure. Double-click ReportFailure and add the following code to the activity’s ExecuteCode event handler:

Console.WriteLine("You need {0} units of product {1} " & _
  "but only {2} are available", 100 - onHand, _
  ProductID, available)
Console.WriteLine("The product has not been reordered" & vbCrLf)
Console.WriteLine("You need {0} units of product {1} " +
  "but only {2} are available", 100 - onHand,
  ProductID, available);
Console.WriteLine("The product has not been reordered\n");

Select View | Designer to return to the workflow designer. The workflow should look like Figure 8.

Figure 8. The workflow should look like this.

Save and press Ctrl + F5 to run your project. You should first see the following output:

There are only 10 units of product 2 on hand
It is time to reorder
90 units of product 2 will be ordered
Press any key to continue . . .
Press any key to exit the application.

To see what happens if there is insufficient availability for a reorder, make the following modification to the Main method:

parameters.Add("ProductID", 3)
parameters.Add("ProductID", 3);

Save and press Ctrl + F5 to run your project. You should see the following output:

There are only 25 units of product 3 on hand
It is time to reorder
You need 75 units of product 3 but only 50 are available
The product has not been reordered
Press any key to continue . . .
Press any key to exit the application.

Introducing the While Activity

The While activity allows you to control how many times an activity or a sequence of activities executes. The While activity is a container and can contain a single activity. You might think that severely limits its usefulness until you understand that the single activity can be a Sequence activity (or other composite activity), which can contain as many activities as you want.

Similar to a while loop construct in code, as long as the While activity’s condition evaluates to true, the workflow will execute the activity or activities it contains. The workflow will continue to do this until the condition evaluates to false.

For your next exercise in this tutorial, you will modify the workflow you created in the previous exercise. Rather than process one item at a time, you will have the workflow process all items. You will use a While activity and the condition will indicate whether there are items remaining.

Modify the Workflow

Return to Visual Studio 2008, and open Workflow1 in the workflow designer. In the previous exercise, you used a Code activity to read an XML file. Because you are now iterating all items in the inventory, you need to modify the workflow to load in the entire file and iterate through it. To load the file into memory, you will use the Initialized event of the workflow. This event occurs after the workflow starts but before it executes any activities. In the Properties window, locate the Initialized property. Type WorkflowInititialized and press Enter; Visual Studio creates the Initialized event handler and open the code editor.

Remove the code that defines the ProductID property since the workflow no longer works with a single ID. Then add the following declarations to the workflow class:

Private inventory As IEnumerable(Of XElement) = Nothing
Public itemCount As Integer = 0
Public nextItemNumber As Integer = 0
Private productID as Integer = 0
private IEnumerable<System.Xml.Linq.XElement> inventory = null;
public int itemCount = 0;
public int nextItemNumber = 0;
private int productID = 0;

The inventory field will store the contents of the inventory list. You declare this as IEnumerable(Of XElement) so that you can query the Inventory.xml file using LINQ to XML. You will write that code next. The remaining fields in the declaration will store the number of items in the inventory list, the next item number in the list and the ID of that item.

Replace all instances of Me.ProductID or this.ProductID in your code with productID.

Add the following code to the WorkflowInitialized method:

Dim xmlFile = XDocument.Load(IO.Path.Combine( _
  AppDomain.CurrentDomain.BaseDirectory, "Inventory.xml"))
inventory = From product In xmlFile.Descendants("Product")
itemCount = _
  (From product In xmlFile.Descendants("Product") _
  Select product.Element("ProductID").Value).Count
var xmlFile = System.Xml.Linq.XDocument.Load(
  System.IO.Path.Combine(
  AppDomain.CurrentDomain.BaseDirectory, "Inventory.xml"));
inventory =
  from product in xmlFile.Descendants("Product")
    select product;
itemCount =
  (from product in xmlFile.Descendants("Product")
   select product.Element("ProductID").Value).Count();

In the previous exercise, you used a LINQ To XML query to retrieve inventory information for a single product. In this example, the query retrieves inventory information for all products.

Select View | Designer to return to the workflow designer. Drag a While activity from the Toolbox onto the workflow above the LookupProduct activity. Name the While activity ProcessItems.

In the Properties window, find the Condition property, select the drop-down arrow to the right of the property’s value, and select Declarative Rule Condition.

Click the “+” sign to the left of the Condition property, expanding the property. Set the ConditionName property to MoreItems. Click the Expression property and then click the ellipsis to the right of that property. Enter this.nextItemNumber <= this.itemCount - 1 as the condition. Click OK to close the editor.

ProcessItems still indicates an error because you haven’t added an activity to it yet. From the Toolbox, drag a Sequence activity into ProcessItems. Now select LookupProduct and CheckOnHand and drag them into sequenceActivity1. The workflow should look like Figure 9.

Figure 9. The workflow should look like this.

In the previous exercise, the LookupProduct activity retrieved the XML file and found a single product. You will now change it to retrieve the next item in the inventory list. Double-click LookupProduct and replace the code in the activity’s ExecuteCode event handler with the following:

productID = Convert.ToInt32( _
  inventory(nextItemNumber).Element("ProductID").Value)
onHand = Convert.ToInt32( _
  inventory(nextItemNumber).Element("OnHand").Value)
available = Convert.ToInt32( _
  inventory(nextItemNumber).Element("Available").Value)
nextItemNumber += 1
productID = Convert.ToInt32(
  inventory.ElementAt(nextItemNumber).Element("ProductID").Value);
onHand = Convert.ToInt32(
  inventory.ElementAt(nextItemNumber).Element("OnHand").Value);
available = Convert.ToInt32(
  inventory.ElementAt(nextItemNumber).Element("Available").Value);
nextItemNumber += 1;

Calling the Workflow from the Console Application

Your final step will be to modify the console host application so that it does not pass a product id to the workflow. In the Solution Explorer, double click Module1.vb or Program.cs. Comment out the following code in the Main method:

Dim parameters As New Dictionary(Of String, Object)
parameters.Add("ProductID", 3)
var parameters = new Dictionary<string, object>();
parameters.Add("ProductID", 3);

Make the following change to the code that starts the workflow:

workflowInstance = workflowRuntime.CreateWorkflow( _
  GetType(Workflow1))
WorkflowInstance instance = workflowRuntime.CreateWorkflow(
  typeof(IfElseWhileDemo.Workflow1));

Save and press Ctrl + F5 to run your project. You should see the following output:

There are 100 units of product 1 on hand
There are only 10 units of product 2 on hand
It is time to reorder
90 units of product 2 will be ordered
There are only 25 units of product 3 on hand
It is time to reorder
You need 75 units of product 3 but only 50 are available
The product has not been reordered
Press any key to continue . . .
Press any key to exit the application.

Introducing the Parallel Activity

You can use the Parallel activity to execute two or more branches of activities in parallel. They do not execute literally in parallel. The workflow executes an activity in the first branch and then switches to an activity in the next branch, and so on. You can have as many branches as you want and each branch can contain as many activities as you want. In addition, the branches do not need to contain the same activities. Often they will, but it is not a requirement.

For your final exercise in this tutorial, you will create a new sequential workflow project to model an auction. This auction will consist of three bidders, each represented by a Parallel activity in the workflow. Each bidder has a budget. In each round of the auction, each bidder ups the current bid by half of his or her remaining budget. The auction continues until two of the bidders are out of money. The third bidder wins the auction.

Create a Workflow Project

To get started, in Visual Studio 2008 select File | New | Project to display the New Project dialog box. In the list of project types, select Workflow, displaying the list of templates. For this demonstration, select the Sequential Workflow Console Application template, name your application ParallelDemo, and select an appropriate folder for the project. Click OK to create the project.

Select View | Code. Now add the following declarations to store the price of the item the workflow will auction and the budgets of the three bidders:

Private budget1 As Decimal = 5000D
Private budget2 As Decimal = 10000D
Private budget3 As Decimal = 25000D
Private nextBid1 As Decimal = 500D
Private nextBid2 As Decimal = 1000D
Private nextBid3 As Decimal = 2500D
Public outOfMoney1 As Boolean = False
Public outOfMoney2 As Boolean = False
Public outOfMoney3 As Boolean = False
Private currentBid As Decimal = 100D
Public biddersOut As Integer = 0
Private lastBidder As Integer = 0
private decimal budget1 = 5000M;
private decimal budget2 = 10000M;
private decimal budget3 = 25000M;
private decimal nextBid1 = 500M;
private decimal nextBid2 = 1000M;
private decimal nextBid3 = 2500M;
public bool outOfMoney1 = false;
public bool outOfMoney2 = false;
public bool outOfMoney3 = false;
private decimal currentBid = 100M;
private int lastBidder = 0;
public int biddersOut = 0;

The first three fields set the available budget for the three bidders. The next three fields keep track of the amount by which each bidder will increase the current bid when it is their turn. The next three fields keep track of whether each bidder is out of money, meaning they do not have enough to up the current bid. The currentBid field represents the current bid. The lastBidder field keeps track of which bidder bid last. The biddersOut field keeps track of how many bidders are finished bidding.

Add Activities to the Workflow

Select View | Designer to return to the workflow designer. Drag a Code activity From the Toolbox onto the workflow. Name this activity StartBidding. Double-click StartBidding and add the following code to the activity’s ExecuteCode event handler:

Console.WriteLine("The starting bid is {0:C}" & vbCrLf, _
  currentBid)
Console.WriteLine("The starting bid is {0:C}\n" , currentBid);

Select View | Designer to return to the workflow designer. From the Toolbox, drag a While activity onto the workflow below StartBidding. Name the While activity Bidding.

In the Properties window, find the Condition property, select the drop-down arrow to the right of the property’s value, and select Declarative Rule Condition.

Click the “+” sign to the left of the Condition property, expanding the property. Set the ConditionName property to BiddersRemaining. Click the Expression property and then click the ellipsis to the right of that property. Enter this.biddersOut < 2 as the condition. Click OK to close the editor.

From the Toolbox, drag a Parallel activity into StartBidding. From The Parallel activity contains two Sequence activities by default. To add a third Sequence activity, right click ParallelActivity1 and select Add Branch. Name the three Sequence activities Bidder1, Bidder2 and Bidder3. The workflow should look like Figure 10.

Figure 10. The workflow should look like this.

Each branch in the Parallel activity represents one of the three bidders. Inside each branch you will model the process of bidding. If the bidder has money left he or she will increase the current bid by a certain amount.

Drag an IfElse activity from the Toolbox into Bidder1. Right click on ifElseBranchActivity2 and select Delete. Rename ifElseBranchActivity1 to IfStillBidding1.

In the Properties window, find the Condition property, select the drop-down arrow to the right of the property’s value, and select Declarative Rule Condition.

Click the “+” sign to the left of the Condition property, expanding the property. Set the ConditionName property to NotOutOfMoney1. Click the Expression property and then click the ellipsis to the right of that property. Enter !this.outOfMoney1 as the condition. Click OK to close the editor.

Copy ifElseActivity1 and paste it into Bidder2. Rename ifElseBranchActivity1 to IfStillBidding2. Click the “+” sign to the left of the Condition property, expanding the property. Set the ConditionName property to NotOutOfMoney2. Click the Expression property and then click the ellipsis to the right of that property. Enter !this.outOfMoney2 as the condition. Click OK to close the editor.

Copy ifElseActivity1 and paste it into Bidder3. Rename ifElseBranchActivity1 to IfStillBidding3. Click the “+” sign to the left of the Condition property, expanding the property. Set the ConditionName property to NotOutOfMoney3. Click the Expression property and then click the ellipsis to the right of that property. Enter !this.outOfMoney3 as the condition. Click OK to close the editor.

After you’ve accomplished the above, the Parallel activity should look like Figure 11.

Figure 11. The Parallel activity should look like this.

Finishing the Parallel Workflow

To complete the workflow, you will now add the custom code activities into each of the branches to execute the business logic; in this case, you will have the bidders place their bids.

From the Toolbox, drag a Code activity into IfStillBidding1. Name the activity Bid1. Double-click Bid1 and add the following code to the activity’s ExecuteCode event handler:

If Not (outOfMoney2 AndAlso outOfMoney3) Then
  If currentBid + nextBid1 > budget1 Then
    outOfMoney1 = True
    biddersOut += 1
    Console.WriteLine("Bidder 1 is out" & vbCrLf)
  Else
    currentBid += nextBid1
    lastBidder = 1
    Console.WriteLine("Bidder 1 bids {0:C}" & vbCrLf, _
      currentBid)
  End If
End If
if (!(outOfMoney2 && outOfMoney3))
{
  if (currentBid + nextBid1 > budget1)
  {
    outOfMoney1 = true;
    biddersOut += 1;
    Console.WriteLine("Bidder 1 is out\n");
  }
  else
  {
    currentBid += nextBid1;
    lastBidder = 1;
    Console.WriteLine("Bidder 1 bids {0:C}\n", currentBid);
  }
}

Select View | Designer to return to the workflow designer. From the Toolbox, drag a Code activity into IfStillBidding2. Name the activity Bid2. Double-click Bid2 and add the following code to the activity’s ExecuteCode event handler:

If Not (outOfMoney1 AndAlso outOfMoney3) Then
  If currentBid + nextBid2 > budget2 Then
    outOfMoney2 = True
    biddersOut += 1
    Console.WriteLine("Bidder 2 is out" & vbCrLf)
  Else
    currentBid += nextBid2
    lastBidder = 2
    Console.WriteLine("Bidder 2 bids {0:C}" & vbCrLf, _
      currentBid)
  End If
End If
if (!(outOfMoney1 && outOfMoney3))
{
  if (currentBid + nextBid2 > budget2)
  {
    outOfMoney2 = true;
    biddersOut += 1;
    Console.WriteLine("Bidder 2 is out\n");
  }
  else
  {
    currentBid += nextBid2;
    lastBidder = 2;
    Console.WriteLine("Bidder 2 bids {0:C}\n", currentBid);
  }
}

Select View | Designer to return to the workflow designer. From the Toolbox, drag a Code activity into IfStillBidding3. Name the activity Bid3. Double-click Bid3 and add the following code to the activity’s ExecuteCode event handler:

If Not (outOfMoney1 AndAlso outOfMoney2) Then
  If currentBid + nextBid3 > budget3 Then
    outOfMoney3 = True
    biddersOut += 1
    Console.WriteLine("Bidder 3 is out" & vbCrLf)
  Else
    currentBid += nextBid3
    lastBidder = 3
    Console.WriteLine("Bidder 3 bids {0:C}" & vbCrLf, _
      currentBid)
  End If
End If
if (!(outOfMoney1 && outOfMoney2))
{
  if (currentBid + nextBid3 > budget3)
  {
    outOfMoney3 = true;
    biddersOut += 1;
    Console.WriteLine("Bidder 3 is out\n");
  }
  else
  {
    currentBid += nextBid3;
    lastBidder = 3;
    Console.WriteLine("Bidder 3 bids {0:C}\n", currentBid);
  }
}

Select View | Designer to return to the workflow designer. From the Toolbox, drag a Code activity into the workflow below Bidding. Name the activity DeclareWinner. Double-click DeclareWinner and add the following code to the activity’s ExecuteCode event handler:

Console.WriteLine("Bidder {0} wins with a bid of {1:C}" & _
  vbCrLf, lastBidder, currentBid)
Console.WriteLine("Bidder {0} wins with a bid of {1:C}\n",
  lastBidder, currentBid);

Save and press Ctrl + F5 to run your project. You should see the following output:

The starting bid is $100.00
Bidder 1 bids $600.00
Bidder 2 bids $1,600.00
Bidder 3 bids $4,100.00
Bidder 1 bids $4,600.00
Bidder 2 bids $5,600.00
Bidder 3 bids $8,100.00
Bidder 1 is out
Bidder 2 bids $9,100.00
Bidder 3 bids $11,600.00
Bidder 2 is out
Bidder 3 wins with a bid of $11,600.00
Press any key to continue . . .
Press any key to exit the application.

Extra Credit: Comparing the Parallel and the Replicator Activities

You have just seen the basics of how the Parallel activity works. The workflow processes each parallel branch in turn, starting with the first branch and continuing through to the last. In this example, the parallel activity evaluates a condition and then executes the Code activity if the condition evaluates to true. In this simple example, each bidder has the same strategy, so you used the same activities in each branch, using basically the same code for each bidder.

In the next tutorial in this series, you will learn about the Replicator activity, which provides an alternative way to construct this workflow logic with less duplication of effort. The Replicator activity is the functional equivalent of a ForEach activity, and provides you with the ability to execute one or more activities for each member of a collection. If you used the Replicator here, you would have passed the three bidders as a collection to a Sequence activity containing an IfElse and Code activity within the Replicator. At runtime, the workflow would execute the activities for each bidder in turn. You are encouraged to work through the Replicator tutorial and then redo this example.

Extra Credit: How WF Works with Multiple Activities in a Parallel Branch

In this example, each branch of the Parallel activity contained the same activities. You can therefore reliably expect the workflow to process each branch completely before processing the next branch. What if one branch had a different set of activities? For example, suppose the second bidder reevaluated his or her strategy each round prior to bidding. You might then add a Code activity named Reevaluate before the IfElse activity in the second branch.

At runtime, the workflow would first execute the Bid1 activity. It would then execute the Reevaluate activity. It would then execute the Bid3 activity, followed by Bid1 and then it would execute the Bid2 activity. The workflow does not execute all of the activities in each branch before moving on to the next branch. This behavior is by design, and has to do with the way the workflow runtime schedules activities for execution. This can be very helpful when coordinating work across a series of asynchronous processes/services where you can initiate the work in your parallel branches, and then gather them back up. But this topic goes beyond the scope of this tutorial, and the subject of its own article.

If you wanted to have branch 2 execute completely before moving onto branch 3 - how would you construct this workflow? You could use a SynchronizationScope activity in branch 2 rather than a Sequence activity. The SynchronizationScope activity executes the activities in it in a sequential fashion. An easier solution would be to use the Replicator, because it completely executes its contained activities before moving on to the next item in the collection.

Conclusion

In this tutorial, you learned how to use the IfElse, While and Parallel activities to control the flow of a workflow. You saw how to use these activities to specify when activities execute and for how long. For the sake of simplicity, you used custom code activities in command line applications; in your workflows, you will typically use custom activities in Windows or web applications, or running services. As you use these control flow activities to drive your WF workflows, one concern that is raised by WF developers is when to use the activity-based control flow and when to use code-based control flow.

The IfElse activity performs a similar purpose to the If..Else (Visual Basic) and if..else (C#) blocks in code. For the IfElse activity, the decision of when to use an IfElse activity or perform branching in code tends to depend on what action you want to take. If the action involves one or more activities, then you will use the IfElse activity. If you can perform the action in code, then you will typically perform the branching within the code of your custom activity.

The same applies to the While activity, which performs a similar purpose to several language constructs that support looping. If you simply want to execute five lines of code repeatedly, you should just use a Code activity and perform the looping in code. However, if you want to execute multiple activities repeatedly, then you should use a While activity.

About the Author

Robert Green is a developer, writer, and trainer. He is a senior consultant with MCW Technologies. Robert is a Visual Studio Tools for the Office system MVP and is a co-author of AppDev courseware for Microsoft Visual Basic, Microsoft Visual C#, LINQ and Microsoft Windows Workflow Foundation. Before joining MCW, Robert worked at Microsoft as a Product Manager and also a Program Manager.