Windows Workflow Foundation Essentials

t can be tough keeping up with all the new technologies released by Microsoft, but Windows Workflow Foundation (WF) is a technology you do not want to miss. This article demonstrates the benefits of Windows Workflow and invites you to roll up your sleeves and get started creating your first basic workflows.

Windows Workflow Foundation (WF) is a general-purpose programming framework for creating reactive programs where business rules and flow of control are represented graphically. As you shall see, the WF programming model separates the what from the when.

WF is one of four technologies?along with Windows Presentation Foundation (WPF), Windows Communication Foundation (WCF), and Windows CardSpace?released by Microsoft as part of the .NET 3.0 Framework. The “3.0” designation is confusing to many developers because it seems to indicate that these technologies must be used with a new version of the .NET Framework. In reality, they work with (and currently depend on) version 2.0 of the .NET Framework as shown in Figure 1.

?
Figure 1. NET 3.0 Dependencies: Although counterintuitively named, the .NET 3.0 technologies work with and depend on version 2.0 of the .NET Framework.

The problem with the 3.0 moniker is that some development shops choose not to use these new technologies because they think they need to install a new version of the .NET Framework and don’t want to make that shift in the middle of their current development cycle. Once again, Microsoft has shot itself in the foot with another naming faux pas.

In reality, these 3.0 technologies are all add-on technologies that work with the .NET 2.0 Framework and Visual Studio 2005. You can use these technologies in the same way that you use third-party add-ons for the .NET 2.0 Framework.

Windows Workflow Benefits
From a pragmatic perspective, new technologies must be ready for prime time and provide tangible benefits before they’re useful. WF hits the mark on both points. Of all the .NET 3.0 technologies, WF is the most complete in terms of functionality and design-time experience.

Here are some of the benefits you reap from WF:

  • It provides a higher level of abstraction and visual representation of your business processes that make them easy to create and understand?both by developers and business domain experts.
  • It’s easy to change the flow and rules associated with business processes, often without having to recompile.
  • WF programming encourages developers to create a core set of well-tested activities that you can reuse in multiple applications.
  • Compared to their UML counterpart?Activity Diagrams?WF diagrams are first-class software artifacts that don’t become outdated and diverge from business process logic because they are the business process logic.
  • The Windows Workflow runtime provides a robust, scalable environment in which your workflows execute. For long-term processes, workflows can be persisted to a database when they become idle (optional) and reactivated when an external stimulus occurs.

Activity-Oriented Programming
WF introduces .NET developers to the concept of activity-oriented programming. This programming model focuses on creating application functionality by combining sets of activities to accomplish a larger business process.

As you can see in the partial workflow shown in Figure 2, a workflow diagram contains activities such as validateProductActivity, sellProductActivity, backOrderProductActivity, and shipOrderActivity, each of which executes a single unit of work in a workflow.

?
Figure 2. Workflow Diagram: A workflow diagram contains activities, each of which performs one part of a business process.

Using the WF designer you can add activities to a diagram and specify the flow of the work to be executed using conditional branching and other flow-of-control constructs that execute based on rules that you specify. This is a powerful approach that takes your application processing code “out of the weeds” and elevates it to a place where it is easy to understand and configure.

Getting Started in Windows Workflow
To get started with Windows Workflow you need the following products on your development machine (installed in the order listed):

  • .NET 2.0 Framework
  • Visual Studio 2005
  • .NET 3.0 Framework
  • Visual Studio 2005 Extensions for Windows Workflow

The .NET 3.0 Framework has over 350 classes defined in three WF-specific assemblies:

  • System.Workflow.Activities
  • System.Workflow.ComponentModel
  • System.Workflow.Runtime

When you install the Visual Studio 2005 Extensions for Windows Workflow, it adds workflow-specific project templates and item templates to your development machine that you can select when you create a new project or add a new item to a project.

After installing the prerequisites, you’re ready to create your first workflow. The following sections outline the steps involved as well as best practices as you go through each step.

Editor’s Note: This article was first published in the November/December 2007 issue of CoDe Magazine, and is reprinted here by permission.

Sequential vs. State Machine Workflows
You can create two main types of workflows?sequential and state machine. The workflow shown in Figure 2 is a sequential workflow, which is similar to a flowchart or UML Activity diagram. The workflow executes activities in a prescribed order, defined in the workflow itself.

There are two types of diagrams to choose from?sequential and state machine. Sequential workflows are similar to flowchart or UML activity diagrams. State machine diagrams define a set of states with possible transitions between them.

In contrast, state machine workflows define a set of states with possible transitions between them. Events external to the workflow trigger transitions between states. This article covers only sequential workflows; a future article will discuss state machine workflows.

Ultimately, you can represent any sequential workflow as a state machine workflow and vice-versa. However, each type of workflow usually fits best with a particular application. Typically, if human interaction is involved as part of the workflow, a state machine is the best choice.

Creating a Workflow Host Application
Before creating a workflow you need to create a host application in which it will run. To do this, launch Visual Studio 2005, click the File menu, and select New ? Project. In the New Project dialog box’s Project Types pane under your preferred programming language, choose Workflow as shown in Figure 3.

?
Figure 3. New Project Templates: Workflow Extensions for Visual Studio 2005 adds project templates useful in creating Workflow-specific applications and libraries.

?
Figure 4. New Workflow: When you create a new Workflow host application it includes a Sequential Workflow diagram on which you can build.

In the Templates pane on the right, select Sequential Workflow Console Application to create a console application that will host the workflow. Change the name of the project to SequentialWorkflowConsole. After changing the project’s location to a desired directory, click OK to create the workflow project. After your project is created, you’ll see a new, empty sequential workflow, as shown in Figure 4.

As you can see by the diagram text that says “Drop Activities to Create a Sequential Workflow” the diagram is ready for you to add built-in activities or, as you’ll see later, your own custom activities.

Go to the Visual Studio Toolbox and look at the Windows Workflow tab, which contains a predefined set of activities (see Figure 5). You can drag and drop these elements directly on your workflow diagram.

?
Figure 5. Toolbox Tab: The Windows Workflow Toolbox tab contains many useful, ready-to-use activities that provide basic functions and flow-of-control tools for your Workflow diagrams.

Next, go to the Solution Explorer and expand the project’s References node to see the three System.Workflow assemblies that you have referenced in the project. As you might guess, these are references to the .NET 3.0 Windows Workflow assemblies.

In addition to diagrams, Windows Workflow includes a robust rules engine that allows you to define rules associated with your workflow.

To get a closer look at the actual workflow host program, open the Program.cs file (C#) or the Module.vb file (Visual Basic). As you can see in Listing 1 and Listing 2 these files contain the workflow host’s Main() entry method.

Note the two lines that appear after the call to WaitHandle.WaitOne(). These lines display a console message, and then wait for console input. Adding these two lines to your workflow host program causes the console window to remain open after executing the workflow, allowing you to see its results.

Quick Start with the Code Activity
The easiest way to get a simple workflow up and running is to use the Code activity. I’ll show you how to implement a simple workflow with a single Code activity so you can see how to use the workflow designer, workflow toolbox, and understand how the workflow host fits into the equation.

?
Figure 6. Error Icons: Activities can display error icons that indicate conditions that must be met for the activity.

Go to the Solution Explorer, and double-click the Workflow1 diagram to display it in the designer. From the Visual Studio Toolbox, drag a Code activity and drop it on the sequential workflow diagram as shown in Figure 6. You’ll see a red exclamation mark error icon located on the upper-right corner of the Code activity. If you click the icon’s smart tag, a drop-down displays the error: Property “ExecuteCode” is not set. The error indicates that you must set the Code activity’s ExecuteCode property to make the activity functional. Errors such as this are a great feature of the Windows Workflow designer that lets activity authors specify the steps an activity user must take to make the activity functional (usually setting a property or creating an event handler method).

To satisfy this condition of the Code activity and eliminate the error, double-click the activity in the diagram to create an event handler for the activity’s ExecuteCode event. Then, add the following code to the handler?in this case, a simple write operation to the console:

   // C#   private void codeActivity1_ExecuteCode(object sender,   EventArgs e)    {      Console.WriteLine("Hello WF World!");   }         ' Visual Basic   Private Sub codeActivity1_ExecuteCode(ByVal sender As    System.Object, ByVal e As System.EventArgs)       Console.WriteLine("Hello WF World!")   End Sub

Now that you have created a workflow host application and a simple workflow with one activity, you are ready to run it.

Running Your First Workflow
I highly recommend setting a breakpoint at the top of the host application’s Main method so you can step through the instantiation of the workflow runtime and the workflow itself.

In C# open Program.cs, or in Visual Basic open Module.vb, and set a breakpoint on the using statement in the Main() method.

Press F5 to run the workflow host program, and then press F10 to step through each line of the workflow host Main() method. As you step through the code, note these key actions that occur in the method:

?
Figure 7. Completed Workflow: The hosting console application displays the “Hello WF World” message created by the Code activity.
  • First, the WF runtime gets instantiated. This must happen first because WF uses the runtime to instantiate, execute, and manage your workflows.
  • Handler methods get registered for the workflow runtime’s WorkflowCompleted and WorkflowTerminated events. This allows the workflow host program to be notified whenever a workflow completes normally or prematurely terminates.
  • The workflow gets instantiated. Notice that the host program does not instantiate the workflow directly, but by calling the workflow runtime’s CreateWorkflow() method.
  • Finally, the method calls the workflow’s Start() method, which begins executing the workflow.

When the workflow is complete, you will see a console window displaying the text “Hello WF World!” as shown in Figure 7. Press any key to close the console window, and then press F5 to exit the workflow host program.

Using Custom Activities
Although it’s quick and easy to use the Code activity, it’s not a best practice because code directly associated with an activity is not reusable in any other workflow. When you double-click a Code activity, Visual Studio adds an event handler method directly to the workflow diagram. This is similar to double-clicking a user interface control in a Windows Forms application, which adds a handler to the parent form?but then you can use the code only in that form. It’s perfectly acceptable to use the Code activity when you want to add workflow-specific code to a workflow. However, in all other cases it’s best to create custom activities containing logic that you can reuse in many different workflows.

The creation and use of custom activities turns WF into a domain-specific language tool. You can create activities specific to your business domain (such as medical billing, oil and gas, financial, auto parts inventory, and so on) and then use them in multiple workflows. This is a higher and better level of abstraction in which to work.

Designing Your Workflow
In a real-world workflow, to help save time and energy, you should (call me a renegade) spend time up front designing it?determining the activities you need and the basic flow of control at a high level. The design process can be something as simple as a whiteboard drawing. You don’t need to create a formal diagram because the workflow itself is a living diagram!

?
Figure 8. High-Level Design: Before using the Visual Studio Workflow designer to create your workflow, it’s best to create a high-level sketch outlining what you want the workflow to accomplish.

When designing activities for your workflow, you should first determine the activity’s purpose as well as its interface. It’s a best practice to keep your activities self contained (not relying on other activities) so you can reuse them in any workflow.

To demonstrate these principles, you will build a sequential workflow that processes orders. As already mentioned, you should first create a high-level overview of what you want the workflow to accomplish. Figure 8 provides just that.

The high-level “sketch” in Figure 8 shows the different activities that the workflow needs to perform as well as the basic flow of the business process in flowchart format. As you can see, the workflow first validates the customer to determine if that customer can order products (the customer may be over a credit limit). If they can’t order, the program sends an e-mail to them rejecting their order. If the customer can order, the workflow validates each product to see if it’s available. If the product is available, it’s added to their order; otherwise, it’s backordered. When finished, the workflow sends an email invoice to the customer and marks the order for shipment.

Creating Custom Activities
Here’s how the sketch in Figure 8 translates into a WF diagram. The rounded rectangles in the sketch in Figure 8 all translate into activities in a workflow. The decision and flow-of-control elements also easily map to elements in WF. So, first, create an activity that validates the customer.

?
Figure 9. New Default Activity: By default, activities are based on the Sequential activity which can contain multiple child activities.

Right-click your workflow project, and select Add ? New Item from the shortcut menu. In the Add New Item dialog box, select Activity. In the Name text box, change the name of the activity to ValidateCustomerActivity.cs (or .vb). Finally, click Add to add the new activity to Solution Explorer. You’ll see a visual representation of the activity as shown in Figure 9. Notice the new activity contains the name of the activity as well as the text “Drop activities here.” This is because, by default, Visual Studio derives new activities from the WF SequenceActivity class, which can contain child activities.

Author’s Note: It’s a standard naming convention to use the suffix “Activity” on all your WF activities.

In this case, because the activities in this workflow don’t need to contain child activities, you can change the base class of the activity to System.Workflow.ComponentModel.Activity instead. To do this, select the new custom activity in design mode, go to the Properties window, and then select the Base Class property. Click the ellipsis button associated with the property, which launches the “Browse and Select a .NET Type” dialog box. In the left pane of the dialog box, select the System.Workflow.ComponentModel node. In the right pane of the dialog box, select the Activity class as shown in Figure 10, and then click OK. This sets the base class of the custom activity to Activity and changes the design-time appearance of the activity so it no longer indicates that you can add child activities to it as shown in Figure 11.

?
Figure 10. Changing the Base Class: You can easily change the base class of an activity by modifying its Base Class property.
?
Figure 11. The Activity Base Class: Changing an activity’s base class to Activity removes the option to add children, which is often what you want for your custom activities.

Next, go to the Solution Explorer and rename the Workflow1.cs or (.vb) file to ProcessOrderWorkflow.cs or (.vb). Select Yes in the dialog box that asks if you want to rename all project elements.

Now rebuild your project. Afterward, double-click the ProcessOrderWorkflow diagram in the Solution Explorer to display it in design mode. The Workflow Toolbox should now contain your custom activity as shown in Figure 12.

?
Figure 12. Custom Activity: Visual Studio automatically displays your custom activities in the Toolbox for easy access.

In contrast to the generic WF activities that come out of the box, you are creating activities specific to a particular domain?order processing.

Workflow and Business Objects
At this point the custom activity doesn’t do anything. The ValidateCustomerActivity needs to validate that a customer can order products (for example, by ensuring that a customer has not exceeded a credit limit). Rather than placing this code directly in the custom activity, you can put the code in a business object, which is preferable because it allows you to use the same core business logic outside the context of WF. As shown in Figure 13, the workflow can call the services of an activity and, in turn, the activity can call the services of one or more business objects.

?
Figure 13. Calling External Code: Rather than placing all your code in Workflow activities, you can have activities call services of business objects which can be reused outside the context of Windows Workflow.

The sample code for this article contains a business object project that contains very rudimentary business objects. In fact, most of the business object’s methods contain pseudo-code, but overall, they can give you a clear picture of WF best practices.

If you are following the step-by-step instructions in this article, you need to add the Order System Business Objects project to your SequentialWorkflowConsole solution. After downloading the sample code, right-click the SequentialWorkflowConsole solution in the Solution Explorer, and then from the shortcut menu select Add ? Existing Project. Navigate to the sample project and add the existing Order System Business Objects project to your solution. To reference the business objects from the Workflow project, right-click the SequentialWorkflowConsole project and select Add Reference from the shortcut menu. In the Add Reference dialog box, select the Projects tab, select the Order System Business Objects project, and then click OK.

Enhancing Custom Activities
Now you’re ready to enhance the custom activity so it performs some real work. The main purpose of the ValidateCustomerActivity activity is to verify that a customer can order products. The key input this activity needs is the ID of the customer being validated. As output, the activity produces a Boolean value indicating whether the customer can order products. The best way to pass this information to and from the activity is by means of dependency properties.

In Windows Workflow a dependency property is similar to a regular .NET property, but rather than storing its value in a private variable, the workflow runtime stores the value in a dictionary maintained by the workflow. This dictionary lets the Workflow automatically propagate the value of the dependency properties to other activities or to the workflow itself, a process known as activity data binding. Activity data binding helps you create encapsulated activities?because they read and write only to their own properties and don’t directly access the workflow or other activities (and therefore don’t have a strong dependency on them), which makes the activity reusable in multiple workflows.

?
Figure 14. Adding Code Snippets: The Workflow Extensions for Visual Studio 2005 add two code snippets that simplify creating dependency properties and their event handlers.

To create a dependency property for the ValidateCustomerActivity that stores the Customer ID as input, right-click the activity in the Solution Explorer and select View Code from the shortcut menu. Add a new, empty line near the top of the class definition, and type the characters wpd. If you have installed the Visual Studio 2005 extensions for Windows Workflow, typing these characters should display the IntelliSense window with the code snippet selected for creating a dependency property as shown in Figure 14. If you are using C#, press TAB twice (just once if using Visual Basic) to insert the snippet code. In the first code snippet box, type CustomerID as the name of the dependency property. Tab to the next code snippet box (tab twice in VB), and type int (C#) or Integer (VB) as the type of the dependency property. If using VB, you must press Tab again and enter the name of the activity class, which in this case is ValidateCustomerActivity.

Next, tab down to the dependency property description and enter Customer ID. Tab again to enter the next code snippet box, and enter Custom Property as the dependency property category. If you are using C#, press Enter to finish editing the code snippet (for some reason this keystroke doesn’t work with VB snippets).

When you’re finished, your dependency property should look like this in C#:

   public static DependencyProperty       CustomerIDProperty =       System.Workflow.ComponentModel.      DependencyProperty.Register(      "CustomerID", typeof(int),       typeof(ValidateCustomerActivity));      [Description("Customer ID")]   [Category("Custom Property")]   [Browsable(true)]   [DesignerSerializationVisibility(      DesignerSerializationVisibility.Visible)]   public int CustomerID   {      get      {         return ((int)(base.GetValue(            ValidateCustomerActivity.            CustomerIDProperty)));      }      set      {         base.SetValue(ValidateCustomerActivity.            CustomerIDProperty, value);      }   }

And like this in Visual Basic:

   Public Shared CustomerIDProperty As _      DependencyProperty = DependencyProperty.Register( _      "CustomerID", GetType(Integer), GetType( _      ValidateCustomerActivity))        _    _    _    _   Public Property CustomerID() As Integer      Get         Return (CType((MyBase.GetValue( _            ValidateCustomerActivity. _            CustomerIDProperty)), Integer))      End Get      Set(ByVal Value As Integer)         MyBase.SetValue(ValidateCustomerActivity. _            CustomerIDProperty, Value)      End Set   End Property

Note that the Get() and Set() methods of the properties do not retrieve or store values into a private variable. Instead, they call the GetValue() and SetValue() methods on the Activity base class, which stores the value in a dictionary object maintained by the workflow.

You also need to create a dependency property that can hold the Boolean output value of the customer validation. To do this, use the wdp code snippet again to create a second dependency property, but this time name the property CanCustomerOrderProperty, make it of type Boolean, set the description to “Specifies if the customer can order a product,” and set the category to Custom Property. Your dependency property should look like this in C#:

   public static DependencyProperty       CanCustomerOrderProperty =       System.Workflow.ComponentModel.DependencyProperty.      Register("CanCustomerOrder", typeof(Boolean),       typeof(ValidateCustomerActivity));       [Description("Specifies if the customer can order       product")]   [Category("Custom Property")]   [Browsable(true)]   [DesignerSerializationVisibility(      DesignerSerializationVisibility.Visible)]   public Boolean CanCustomerOrder   {      get      {         return ((Boolean)(base.GetValue(            ValidateCustomerActivity.            CanCustomerOrderProperty)));      }      set      {         base.SetValue(ValidateCustomerActivity.            CanCustomerOrderProperty, value);      }   }

And like this in Visual Basic:

   Public Shared CanCustomerOrderProperty As _      DependencyProperty = DependencyProperty. _      Register("CanCustomerOrder", GetType(Boolean), _      GetType(ValidateCustomerActivity))       _    _    _    _   Public Property CanCustomerOrder() As Boolean      Get         Return (CType((MyBase.GetValue( _         ValidateCustomerActivity. _         CanCustomerOrderProperty)), Boolean))      End Get      Set(ByVal Value As Boolean)         MyBase.SetValue(ValidateCustomerActivity. _            CanCustomerOrderProperty, Value)      End Set   End Property

Enhancing Custom Activities (continued)
Now you can add code to the activity that sets the CanCustomerOrder dependency property to the correct value. As mentioned previously, the best approach is to call the services of a business object from the activity. The source code for this article contains a Customer business object that has a CanCustomerOrder() method you can use to determine if a customer can order products. First, you need to go back to the top of the activity’s source code, and add a reference to the business object project’s namespace to the activity.

In C#:

   using OakLeaf.OrderSystem.Business;

And in Visual Basic:

   Imports OakLeaf.OrderSystem.Business

You can add a third dependency property to the activity that can hold a reference to the Customer business object. This time use the wdp code snippet to create a dependency property named CustomerProperty, of type Customer, a description of “Customer Business Object,” and the Custom Property category.

Your property should look like this in C#:

   public static DependencyProperty       CustomerProperty = System.Workflow.      ComponentModel.DependencyProperty.      Register("Customer", typeof(Customer),       typeof(ValidateCustomerActivity));   [Description("Customer Business Object")]   [Category("Custom Property")]   [Browsable(true)]   [DesignerSerializationVisibility(      DesignerSerializationVisibility.Visible)]   public Customer Customer   {      get      {         return ((Customer)(base.GetValue(            ValidateCustomerActivity.            CustomerProperty)));      }      set      {         base.SetValue(ValidateCustomerActivity.            CustomerProperty, value);      }   }

And in Visual Basic:

   Public Shared CustomerProperty As _      DependencyProperty = _      DependencyProperty.Register("Customer", _      GetType(Customer), GetType(Customer))    _    _    _    _   Public Property Customer() As Customer      Get         Return (CType((MyBase.GetValue(Customer.            CustomerProperty)), Customer))      End Get      Set(ByVal Value As Customer)         MyBase.SetValue(Customer. _            CustomerProperty, Value)      End Set   End Property

Now you’re ready to add code to the activity that does the real work of validating a customer. All workflow activities inherit an Execute() method that the workflow automatically calls when it executes the activity. You override this method to add custom code that accomplishes the task required. Here is the code you can add to the ValidateCustomerActivity.

In C#:

   protected override ActivityExecutionStatus    Execute(ActivityExecutionContext executionContext)   {      if (this.Customer == null)      {         this.Customer = new Customer();      }      this.CanCustomerOrder =          Customer.CanCustomerOrder(         this.CustomerID);      return base.Execute(executionContext);   }

And in Visual Basic:

   Protected Overrides Function Execute( _      ByVal executionContext As _      System.Workflow.ComponentModel. _      ActivityExecutionContext) As _      System.Workflow.ComponentModel. _      ActivityExecutionStatus         If Me.Customer Is Nothing Then         Me.Customer = New Customer()      End If         Me.CanCustomerOrder =       Me.Customer.CanCustomerOrder(Me.CustomerID)         Return MyBase.Execute(executionContext)      End Function

This code first checks to see if the Customer property already contains a reference to a Customer business object. In certain contexts the workflow may set the Customer property. In other contexts, if the Customer property isn’t automatically set, the activity instantiates a new instance. The next line of code calls the Customer business object’s CanCustomerOrder() method passing the CustomerID property and storing the result into the activity’s CanCustomerOrder property. You’ll see in just a bit how the workflow sets the CustomerID property. As an aside, if you look at the CanCustomerOrder() method, you’ll see it simply returns a true rather than doing any real checking.

The last line of code in the method calls the base class Execute() method. This method returns an ActivityExecutionStatus value. By default, this base class method returns a value of ActivityExecutionStatus.Closed indicating the activity has finished its work?which is true of this custom activity.

Adding a Custom Activity to the Workflow Diagram
Now you’re ready to add the ValidateCustomerActivity to the workflow. First, rebuild the Visual Studio project. Next, go to the Solution Explorer and double-click ProcessOrderWorkflow. In the workflow diagram, right-click codeActivity1, and select Delete from the shortcut menu, because you no longer need this activity. Next, go to the Visual Studio Toolbox, and under the SequentialWorkflowConsole Components tab you should see the ValidateCustomerActivity. Drag the activity from the toolbox, and drop it on the workflow diagram where it says “Drop Activities to create a Sequential Workflow.”

Enabling Activity Data Binding
So far, you have created a custom activity that uses a CustomerID property to hold a customer ID it receives as input and a CanCustomerOrder property in which it stores the result of the activity. So, how does the workflow set the activity’s CustomerID property? As mentioned earlier, you can do this by means of activity data binding with dependency properties.

First of all, the workflow host program can pass a customer ID to the workflow. The workflow stores this value in its own CustomerID dependency property (which you still need to create), which can in turn be automatically propagated to the activity’s CustomerID property.

To set up this activity data binding, you must first create a workflow-level CustomerID property. To do this, right-click the workflow diagram, and select View Code from the shortcut menu. At the top of the ProcessOrderWorkflow definition, before the constructor, use the wdp snippet again to create a property named CustomerIDProperty of type int or Integer, with a description of “Customer ID” and a category of Custom Property. This is the same as the property you added to ValidateCustomerActivity. If you want to clean things up a bit, you can delete the handler at the bottom of the source code file that contains code that writes “Hello WF World” to the console.

Now, right-click the code and select View Designer from the shortcut menu to display the workflow diagram again. Select validateCustomerActivity in the diagram, and then go to the Visual Studio Properties window. As shown in Figure 15, in addition to the standard Description and Enabled properties you should see the CanCustomerOrder, Customer, and CustomerID custom properties. Notice the small information icon next to these properties. If you hover your mouse pointer over the icons, the tooltip text “Bind Property” appears. The tooltip indicates that these are dependency properties that can participate in activity data binding.

?
Figure 15. Binding Dependency Properties: The Properties window displays a small information icon to indicate dependency properties.
?
Figure 16. Propagating Values: You can use this dialog to set up activity data binding that automatically propagates values from one dependency property to another.

Select the CustomerID property and click its associated ellipsis button to display a dialog box (see Figure 16) that lists the workflow’s dependency properties (there’s only the one CustomerID property you just added) as well as the workflow activities and their associated dependency properties. Select the workflow’s CustomerID property and click OK. This binds the activity’s CustomerID property to the workflow’s CustomerID property. Whenever your application sets the workflow’s CustomerID property, the workflow now automatically propagates the value to the CustomerID property of the validateCustomerActivity activity.

Passing Parameters to the Workflow
The next step is to have the workflow host program pass a customer ID to the workflow. To do this, go to the Solution Explorer and double-click the Program.cs or Module1.vb file. Find the line of code about halfway down that calls the CreateWorkflow() method. Immediately before this line, add the following code that creates a generic Dictionary. Afterward, change the CreateWorkflow() call so it passes the arguments Dictionary.

In C#:

   Dictionary wfArgs?=?new?      Dictionary();   wfArgs.Add("CustomerID", 1001);   WorkflowInstance instance?=      workflowRuntime.CreateWorkflow(typeof(      SequentialWorkflowConsole.ProcessOrderWorkflow),      wfArgs);

And in Visual Basic:

   Dim wfArgs As Dictionary(Of String,?Object)?=?_      New Dictionary(Of String, Object)()   wfArgs.Add("CustomerID", 1001)      Dim workflowInstance As WorkflowInstance   workflowInstance =?_      workflowRuntime.CreateWorkflow(GetType( _      ProcessOrderWorkflow), wfArgs)

Notice this code adds an argument named CustomerID with the hard-coded value 1001 (for demo purposes) to the dictionary. The name of the argument (CustomerID) must exactly match the name of a property on the workflow, or you will get an exception at run time.

Testing Activity Data Binding
Before going further, you should run the workflow to test the activity data binding. You can set two breakpoints?one in code and one on the workflow diagram itself.

To set the breakpoint in code, go to the Solution Explorer, right-click ValidateCustomerActivity, and select View Code. In the Execute() method, set a breakpoint on the code that calls Customer.CanCustomerOrder() (see Figure 17). Next, go to the Solution Explorer again and double-click ProcessOrderWorkflow. Double-click the workflow diagram to open it in the designer, and then right-click the diagram and select Breakpoint ? Insert Breakpoint from the shortcut menu to add a visual breakpoint indicator to the diagram as shown in Figure 18.

?
Figure 17. Setting Workflow Breakpoints: You can set a breakpoint on the workflow diagram itself, which displays a breakpoint icon in the upper left corner of the diagram.
?
Figure 18. Debugging Workflows: Visual Studio supports visual debugging for workflows. As you step through the workflow the currently executing activity is highlighted in yellow.
?
Figure 19. Propagation Successful: Activity data binding takes care of automatically populating the activity’s CustomerID dependency property from the workflow’s CustomerID dependency property.

Press F5 to run the workflow. You will hit the first breakpoint set earlier in the article. Press F5 again to continue. You should hit the diagram breakpoint, which displays a visual debugging cue?a yellow rectangle around the entire diagram?a very nice feature. Press F10 to continue and a yellow highlight bar appears around validateCustomerActivity as shown in Figure 18. Press F10 one more time to hit the next breakpoint. If you hover your mouse pointer over the reference to the CustomerID property, you will see it is set to 1001 as shown in Figure 19. Activity data binding worked! When the workflow stored this value in its CustomerID property, it automatically propagated the value to the activity’s CustomerID property. You can press Shift+F5 to stop the workflow.

Working with Flow of Control Activities
Now that the workflow can validate a customer, you can add a conditional statement to the workflow diagram to check the result of the validation and choose an appropriate course of action.

?
Figure 20. Conditional Activities: The IfElse activity is a composite activity containing child activities that run when their conditions evaluate to true.

To add the conditional statement, from the Solution Explorer, double-click ProcessOrderWorkflow. Go to the Visual Studio Toolbox and under the Windows Workflow tab drag the IfElse activity and drop it on the diagram directly below the validateCustomerActivity as shown in Figure 20. The IfElse activity is a composite activity that contains other activities that are run conditionally. The activity executes the first branch with a true condition (evaluated from left to right).

Notice the red error icon on the activity. If you click the icon’s smart tag, it says Property “Condition” is not set. The error tells you the Condition property of the IfElse activity must be set so it can perform an evaluation. To set it, select the left branch of the IfElse activity, go to the Properties window, and then select the Condition property. If you expand the Condition property’s drop-down, you’ll find two types of conditions available: Code Condition and Declarative Rule Condition (see Figure 21).

?
Figure 21. Declarative Rule: You can specify either a Code Condition or a Declarative Rule Condition on conditional activities such as IfElse.

Code conditions are custom code-containing methods that return either true or false. You can perform any kind of elaborate checking you want in this method. However, the downside of using code conditions is that you can’t modify the condition without recompiling.

?
Figure 22. Creating Declarative Rules: You must set the Condition Name and Condition Expression when defining a declarative rule condition.

In contrast, declarative rule conditions are stored as XML in the workflow’s associated .rules file. Therefore, you can potentially modify the declarative rule without recompiling the workflow.

In this case it makes the most sense to use a declarative rule condition. To do this, select “Declarative Rule Condition” from the Condition property’s drop-down. Click the plus (+) sign next to the Condition property to expand it. You’ll see the ConditionName and Expression properties as shown in Figure 22. Set the ConditionName to CheckIfCustomerValid. Next, select the Expression property and click its associated ellipsis button, which displays the Rule Condition Editor.

If you are a C# developer, enter the following in the Condition edit box as shown in Figure 23, and then click OK:

?
Figure 23. Rule Syntax: You can use either C# or Visual Basic syntax when defining declarative rule conditions in the Rule Condition Editor.
   this.validateCustomer1.CanCustomerOrder

Or for Visual Basic developers:

   Me.validateCustomer1.CanCustomerOrder

Notice after you typed this or Me, IntelliSense popped up, allowing you to select any activity on the workflow diagram, or any property, event, or method of the diagram?another nice feature. Now that you have set the Condition property, note that the IfElse error icon is gone.

Next, change the name of the IfElse branches so they are more descriptive of what’s happening in the workflow. Select the left branch of the IfElse in the designer, and in the Properties window set its Name property to ifCustomerValid. Next, select the right branch of the IfElse, and set its Name property to ifCustomerInvalid. This makes it obvious to anyone looking at the workflow diagram what causes the different branches to execute.

You could go into much greater detail with this workflow, but for this article, just add two new activities that will act as placeholders; one for each branch of the IfElse.

Now, go to the Solution Explorer, right-click the SequentialWorkflowConsole project, and select Add ? New Item from the shortcut menu. In the Templates pane, select Activity, and then in the Name text box, change the name of the activity to ProcessOrderActivity.cs or .vb and click Add. Go to the Properties window and change the Base Class of the new activity to Activity as you did earlier in this article. Now add a second new activity, name it EmailOrderRejectionActivity, and again change its base class to Activity.

?
Figure 24. Conditional Workflow: The completed workflow runs activities depending on the result of the ValidateCustomer activity.

Rebuild the solution and then go to the Solution Explorer and double-click the ProcessOrderWorkflow to display the workflow designer. From the Visual Studio Toolbox SequentialWorkflowConsole Components tab, drag ProcessOrderActivity and drop it on the left branch of the IfElse activity. Then go back to the Toolbox and drag EmailOrderRejectionActivity and drop it on the right branch of the IfElse activity. When you’re finished, your diagram should look like Figure 24.

Testing the Flow of Control
Now you’re ready to test the flow of control in the workflow. Press F5 to begin executing the workflow, and press F10 to step through the workflow again. This time, the yellow trace indicator should go down the left branch of the IfElse because the customer is hard-coded to be valid, proving that the flow of control works.

The key points to take away from this article are, first, that WF provides a visual representation of your business processes, making them easier to understand and configure. It pulls your business logic “out of the weeds” and into a diagram that provides much greater accessibility. Also, as mentioned earlier, unlike UML diagrams, WF diagrams do not become outdated when business processes change, because they are the application’s business processes.

WF also helps separate business logic as defined in activities from flow of control as specified in a workflow diagram. You can reuse your custom activities in multiple workflows and wire them together as needed in workflow diagrams using standard flow-of-control activities.

Even though this article covered a lot of ground, there are still many great features of Windows Workflow that remain to be explored. You should spend some time learning more about creating custom activities, how to take advantage of the dozens of built-in WF activities (including event-driven activities), persisting workflows to a database, and much more.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: