Rob Green
MCW Technologies LLC
Published: November, 2008
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.
.jpg)
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.
.jpg)
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).
.jpg)
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.
.jpg)
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
.jpg)
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.
.jpg)
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).
.jpg)
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.
.jpg)
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.
.jpg)
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.
.jpg)
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.
.jpg)
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.
Related Links