Windows Workflow Tutorial1

  • May 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Windows Workflow Tutorial1 as PDF for free.

More details

  • Words: 9,104
  • Pages: 34
Windows Workflow Tutorial: Introduction to State Machine Workflows Introduction

Workflows model business processes. When you design a workflow, your first task is to identify the steps that occur during the business process. That is true whether the business process is the processing of an order, the calculation of a bonus payment or the processing of a loan application. The business process consists of steps and your job is to define those. Once you have the steps defined, you can use Windows Workflow Foundation (WF) to build a workflow that models the business process. You can build two types of workflows with WF: sequential and state machine workflows. A sequential workflow provides a structured series of steps in which one activity leads to another, and steps generally occur immediately one after another. A step might wait for some event (an email to arrive, for example), but sequential workflows are often used to model processes that operate without human intervention. A state machine workflow provides a set of states. The workflow begins in an initial state and ends when it reaches the completed state. Transitions between the states define the behavior. In general, state machine workflows react to events. The occurrence of an event causes the workflow to transition to another state. Whether you build a workflow as a sequential or state machine workflow depends on how the business manages the process. For example, suppose you need to build a workflow that models processing an order. You determine the following steps are involved: •

The business receives the order, including all necessary information (customer, product, quantity, credit card information, shipping address, etc).



The business checks if the customer has sufficient credit. If so, the workflow continues. If not, the order is canceled and the workflow ends.



The business checks if there is sufficient inventory to fulfill the order. If so, the process continues. If not, the order is canceled and the process ends.



The business asks the credit card company for payment. If the credit card company approves, the process continues. If not, the order is canceled and the process ends.



The item ships to the customer. The order is marked as fulfilled and the process ends.

A sequential workflow is likely the appropriate type of workflow here. The workflow starts when the company receives an order. It then continues through a number of steps until the order is either canceled as fulfilled. The workflow runs from start to finish with no delays. As an alternative, suppose you determine the following steps are involved in processing an order: •

The business receives the order, including all necessary information (customer, product, quantity, credit card information, shipping address, etc).



The business checks if the customer has sufficient credit. If so, the process continues. If not, the order is canceled and the workflow ends.



The business checks if there is sufficient inventory to fulfill the order. If so, the process continues. If not, the order is canceled and the process ends.



If the product is in stock, the process waits for shipping to ship the product.



Just before shipping the product, the business asks the credit card company for payment. If the credit card company approves, the process continues. If not, the order is canceled and the process ends.



After the product ships, the process waits for the customer to acknowledge receiving it. If the product arrives, the process ends. If the customer does not receive the product, the process waits for shipping to resend it. At this point, either the business or the customer can cancel the order, ending the process.

You can successfully implement this business process either as a sequential workflow or as a state machine workflow. To decide, think about what makes it different from the previous scenario. There are several places in the business process where the process needs to pause and wait for some other process to begin. The pause may be short. It may also be long. It could take weeks or months for new inventory to arrive if the product is out of stock. It hopefully only takes days, not weeks, for shipping to send the product. It will take anywhere from one 1

day to ten days for the product to arrive to the customer. During these periods, there is nothing for the business process to do except wait. One of the main benefits of the state machine workflow is the ability to define states and to define how the workflow moves from state to state. You can define multiple paths through the workflow. If the product is in stock, the workflow can take the following path: Waiting For Shipping -> Waiting For Acknowledgement -> Completed

If the product does not arrive, the workflow can take the following path: Waiting For Shipping -> Waiting For Acknowledgement -> Waiting For Shipping -> Waiting For Acknowledgement -> Completed

You can easily include looping and re-execution of states in a state machine workflow. It is difficult, and potentially not possible, to do this in a sequential workflow. A sequential workflow moves to the next activity when it is finished executing the previous activity. A state machine workflow typically moves to a different state when an external action occurs. This external action can be the host application raising an event handled by the workflow. The action can also be the host application programmatically setting the next state. You can also use the SetState activity in the workflow to move to a new state. Create a State Machine Workflow

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 shown in Figure 1.

Figure 1. Visual Studio 2008 provides these workflow templates. For this demonstration, select the State Machine Workflow Console Application template, name your application StateMachineDemo1, and select an appropriate folder for the project. Click OK to create the project. At this point, the workflow contains a single activity, named Workflow1InitialState, as shown in Figure 2.

2

Figure 2. A new state machine workflow contains a single activity. A state machine workflow contains workflow activities, as does a sequential workflow. You will find that you use workflow activities such as Code, While, IfElse and Sequence in the same manner regardless of the type of workflow you build. There are four activities unique to state machine workflows. These are the State, SetState, StateInitialization and StateFinalization activities (see Figure 3).

Figure 3. These activities are specific to state machine workflows. The State activity is the main activity you will use in a state machine workflow. Each State activity in a workflow represents a point where the business process waits for something to happen. WF requires that your state machine workflows have one state marked as the initial state. Although it is not required, you should also mark one state as the completed state. When the workflow starts, the workflow starts with the state marked as the initial state. You can visually identify the initial state by a green circle in the 3

activity’s title bar. When the workflow reaches the completed state, the workflow will end. You can visually identify the completed state by a red circle in the activity’s title bar. Define the Workflow States

Select the Workflow1InitialState activity and change its name to Started. Notice that the green circle disappears from the title bar. To mark this activity as the initial state, right click on it and select Set as Initial State. The green circle reappears. Note that you can also set the InitialStateName property of the workflow to Started. To add another state to the workflow, drag a State activity from the toolbox onto the design surface. Name the activity Working. Now add the final state to the workflow, naming it Finished. To mark it as the completed state, right click on it and select Set as Completed State. You should now see a red circle in the activity’s title bar. Note that you can also set the CompletedStateName property of the workflow to Finished. The workflow designer now looks like Figure 4. (The Started activity has been widened to see all of the text.)

Figure 4. The state machine workflow contains an initial state, a completed state and an additional state. The next step in building this workflow is to specify, for each state, what state or states the workflow can move to next and what happens while the workflow is in that state, including what happens to move it to another state. State Transitions: Moving from State to State

If this were a sequential workflow, the workflow would execute the activity at the top of the workflow (Started) and then move to the next activity (Working) and then move to the final activity (Finished) and then end. Sequential workflows execute from top to bottom. You set Started as the initial state so when this workflow starts, it will start in the Started state. You set Finished as the completed state so when the workflow gets to the Finished state it will end.

4

How does the workflow move off the Started state? Does it go to Working or Finished next? If it goes to the Working state, how does it move off Started? When it moves off Working, can it go back to Started or can it only go to Finished? To move the workflow to a new state, you use the SetState activity. To specify the next state, you set the TargetStateName property of the SetState activity to that state. Define the Activities that Occur in Each State

State machine workflows move from state to state. While they are in each state (except for the completed state), they can perform actions, wait for an external event to occur and transition to another state. You can add four activities to a State activity: •

The StateInitialization activity, if it exists, is the first activity the workflow executes when it enters a state. This is a Sequence activity, so you can add to it all of the activities you want to execute when the workflow enters that state. You might log the date and time. You might query a database. You could add a SetState activity to a StateInitialization activity if you want the workflow to move to a new state after performing the logging and querying.



In a state machine workflow, the workflow will enter a state and stay there until something happens. This is the essence of a state machine workflow. That something could be internal to the workflow or it could be an external event. You handle external events using an EventDriven activity. It is also a Sequence activity, so you can add to it all of the activities you want to execute when the host raises the event. One of these activities can be a SetState activity.



The StateFinalization activity, if it exists, is the last activity the workflow executes as it leaves a state. It is also a Sequence activity, so you can add to it all of the activities you want to execute before the workflow moves to a new state. You might log the date and time, query a database or clean up resources.



You can add a State activity within a State activity. This creates a hierarchical state machine workflow. You might do this if you had a number of states and each of them reacted to the same events. Rather than set up the events for each state, you could set up the event for one state and then add the other states to that state. The child states would inherit the event driven behavior of the parent state. This is beyond the scope of this tutorial.

Complete the Workflow

You will now finish this workflow. You will define what happens in each state and how the workflow moves from state to state. From the toolbox, drag a StateInitialization activity into the Started activity. stateInitializationActivity1 is a Sequence activity. To add activities to it, double click on it. The workflow designer looks like Figure 5.

Figure 5. The StateInitialization activity is a Sequence activity. Notice the navigation list, or “breadcrumb” at the top left in the designer. This will enable you to return to the main workflow view when you are finished adding activities to the StateInitialization activity.

5

Drag a Code activity from the toolbox into stateInitializationActivity1. Double-click codeActivity1, creating the activity’s ExecuteCode event handler. In the codeActivity1_ExecuteCode method, add the following code to display the order details: Visual Basic

Console.WriteLine("The workflow has started" & vbCrLf) C#

Console.WriteLine("The workflow has started\n");

Select View | Designer to return to the workflow designer. Drag a SetState activity from the toolbox into stateInitializationActivity1 below codeActivity1. Select Working from the drop-down list associated with the TargetStateName property. The workflow now looks like Figure 6.

Figure 6. The SetState activity transitions the workflow to another state. Select the link for Started or Workflow1 in the navigation menu. This returns you to the main workflow view (see Figure 7). Notice that there is now a line connecting Started to Working. This shows you the flow of the workflow from the initial state to the next state.

6

Figure 7. The line indicates the flow of the workflow from state to state. Handle External Events

So far, this workflow executes in a relatively sequential fashion. The workflow starts and then, after displaying a message, moves to the next state. That scenario doesn’t appear very stateful, does it? As mentioned previously, the typical scenario for a state machine workflow it enters a state and waits for the host application to raise an event. You handle external events using an EventDriven activity. You will then decide what actions the workflow takes, including what state it moves to next. From the toolbox, drag an EventDriven activity into Working. Select eventDrivenActivity1. Hover the mouse over the middle square on the left or right border of the activity. The cursor should change to a crosshair (see Figure 8). Hold down the mouse button. Drag the crosshair and drop it on Finished (see Figure 9). There will then be a line connecting the two states.

Figure 8. Hover the mouse over the activity and wait for the cursor to change to a crosshair.

7

Figure 9. Drag an EventDriven activity from one state to another to define a transition. Double click eventDrivenActivity1. You can see that it is a container activity and already includes a SetState activity. When you dragged the crosshair from Working to Finished, Visual Studio added a SetState activity to eventDrivenActivity1 and set its TargetStateName property to Finished (see Figure 10).

Figure 10. The EventDriven activity contains a SetState activity. At this point, the EventDriven activity indicates an error. The first activity in an EventDriven activity must implement the IEventActivity interface. This interface enables activities to subscribe to events. The HandleExternalEvent and Delay activities implement this interface. Use the HandleExternalEvent activity when the host application raises an event. To do this, you first need to create an interface that defines the events the host can raise. You also need to create a class that defines the information the host will pass to the workflow when it raises events. You then associate the HandleExternalEvent activity with an event. This is too advanced for this tutorial, but it will be covered in a later tutorial. For this workflow, you will use a Delay activity and simply pause the workflow for a few seconds. Although this is not a realistic scenario, it serves the purpose here. From the toolbox, drag a Delay activity into eventDrivenActivity1 above setStateActivity2. Set the TimeoutDuration property of delayActivity1 to five seconds. 8

Drag a Code activity from the toolbox into eventDrivenActivity1 between the two activities currently in there. Double-click codeActivity2 and add the following code to the activity’s ExecuteCode event handler: Visual Basic

Console.WriteLine("Moving on now" & vbCrLf) C#

Console.WriteLine("Moving on now\n");

Select View | Designer to return to the workflow designer. Select the link for Workflow1 or Working in the upper left of the workflow designer. From the toolbox, drag a StateFinalization activity into Working below eventDrivenActivity1. To add activities to stateFinalizationActivity1, double click on it. Drag a Code activity from the toolbox into stateFinalizationActivity1. Double-click codeActivity3, creating the activity’s ExecuteCode event handler. In the codeActivity3_ExecuteCode method, add the following code: Visual Basic

Console.WriteLine("Transitioning to Finished" & vbCrLf) C#

Console.WriteLine("Transitioning to Finished\n");

Select View | Designer to return to the workflow designer. Select the link for Workflow1 or Working in the upper left of the workflow designer. The workflow should look like Figure 11.

Figure 11. The workflow with three states and the transitions between them. Call the Workflow from the Console Application

To review the code that actually does the work starting up your workflow, in the Solution Explorer window, double-click Module1.vb or Program.cs. There, you’ll find the following code: Visual Basic

Using workflowRuntime As New WorkflowRuntime()

9

' Code removed here… Dim workflowInstance As WorkflowInstance   workflowInstance = _     workflowRuntime.CreateWorkflow(GetType(Workflow1))   workflowInstance.Start()   ' Code removed here… End Using C#

using (WorkflowRuntime workflowRuntime = new WorkflowRuntime()) {   // Code removed here…   WorkflowInstance instance = workflowRuntime.CreateWorkflow(     typeof(StateMachineDemo1.Workflow1));   instance.Start();   // Code removed here… }

This code, which runs as your application loads, starts by creating a new instance of the WorkflowRuntime class. This class provides an execution environment for workflows. The WorkflowRuntime instance creates an instance of the state machine workflow. Save and press Ctrl + F5 to run your project. If you’ve followed the directions carefully, you should first see the following output: Output

The workflow has started There should then be a five second delay before you see the following output: Moving on now Transitioning to Finished Press any key to continue... Press any key to exit the application. Build a More Useful Workflow

This workflow demonstrates the basics of building a state machine workflow, but it doesn’t really do much. For the remainder of this tutorial, you’ll modify the workflow to model an order processing workflow. When the business receives an order, the host application will start the workflow, passing to it the order information. The workflow will check if there is sufficient inventory to process the order. If not, the order is canceled. If the item is in stock, the workflow will wait for the order to ship. After the order ships, the workflow ends. Change the name of the Working state to WaitingForShipping. The workflow enters this state and waits for something, so why not identify what it is waiting for in the name? Next, you will add properties to the workflow so it can receive order information. Select View | Code. Outside the methods, but inside the Workflow1 class, add the following properties. You’ll use these to keep track of order details, as well as the status of the inventory check: Visual Basic

10

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 Private quantityValue As Integer Public Property Quantity() As Integer   Get     Return quantityValue   End Get   Set(ByVal value As Integer)     quantityValue = value   End Set End Property Public inStock As Boolean = False C#

public int ProductID { get; set; } public int Quantity { get; set; } public bool inStock = false;

Change the code in the codeActivity1_ExecuteCode method to display the order details and check if the item is in stock: Visual Basic

Console.WriteLine("Order received") Console.WriteLine("

Product ID: {0}", Me.ProductID)

Console.WriteLine("

Quantity: {0}" & vbCrLf, Me.Quantity)

inStock = Me.Quantity <= 10 C#

Console.WriteLine("Order received"); Console.WriteLine("

Product ID: {0}", this.ProductID);

Console.WriteLine("

Quantity: {0}\n", this.Quantity);

inStock = (this.Quantity <= 10);

In a production workflow, you would likely query a database to see if the item is in stock. For this tutorial, a quantity of greater than ten will exceed the amount of the item in stock. Select View | Designer to return to the workflow designer. Double click stateInitializationActivity1 to add additional activities. 11

Notice that setStateActivity1 indicates an error. This is because you changed the name of the state to which it transitions. To fix this, change the TargetStateName property to WaitingForShipping. Drag an IfElse activity from the toolbox into the stateInitializationActivity1activity below codeActivity1 but above setStateActivity1. The workflow should now look like Figure 12.

Figure 12. The stateInitializationActivity1activity should now contain these activities. The IfElse activity enables branching in a workflow. It 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 evaluates the condition of the next branch, if there is one. If none of the branch conditions evaluates to true, the activities in the final branch execute (unless that activity has a condition that evaluates to false). At this point, the first IfElseBranch activity indicates an error. You haven’t told it yet what condition to evaluate. To do that, select ifElseBranchActivity1. In the Properties window, find the Condition property, select the dropdown arrow to the right of the property’s value, and select Declarative Rule Condition (see Figure 13). 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 tutorial, you’ll create the condition in the Properties window. 12

Figure 13. 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 itemIsInStock. (Naming the condition allows you to use the same condition 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 14. Click OK to close the editor.

Figure 14. Create the IfElseBranch activity’s rule condition. Drag setStateActivity1 into ifElseBranchActivity1. You have now set up the workflow to transition to the WaitingForShipping state if the item is in stock. You will next tell the workflow what to do if the item is not in stock. Drag a Code activity from the toolbox into the ifElseBranchActivity2 activity. Double-click codeActivity4 and add the following code to the activity’s ExecuteCode event handler: Visual Basic

Console.WriteLine("The item is not in stock") Console.WriteLine("The order is canceled" & vbCrLf) C#

Console.WriteLine("The item is not in stock"); Console.WriteLine("The order is canceled\n");

13

Select View | Designer to return to the workflow designer. Drag a SetState activity from the toolbox into ifElseBranchActivity2 below codeActivity4. Set the TargetStateName property of setStateActivity3 to Finished. The workflow should now look like Figure 15.

Figure 15. The workflow executes these activities when it starts. To recap, if the item is in stock, the workflow transitions to the WaitingForShipping state. If the item is not in stock, the workflow transitions to the Finished state and ends. Select the link for Workflow1 or Started in the upper left of the workflow designer. Notice that there is now a line from Started to Finished (see Figure 16). The workflow takes this path if the item is not in stock.

14

Figure 16. The lines indicate the updated flow of the workflow from state to state. Next, change the messages the workflow displays. Select View | Code. Change the code in the codeActivity2_ExecuteCode method to indicate the workflow is waiting for the item to ship: Visual Basic

Console.WriteLine("The item has shipped" & vbCrLf) C#

Console.WriteLine("The item has shipped\n");

Change the code in the codeActivity3_ExecuteCode method to indicate the item has shipped: Visual Basic

Console.WriteLine("The order is complete" & vbCrLf) C#

Console.WriteLine("The order is complete\n");

Your final step will be to modify the console host application to pass order information 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: Visual Basic

Dim parameters As New Dictionary(Of String, Object) parameters.Add("ProductID", 1) parameters.Add("Quantity", 10) workflowInstance = workflowRuntime.CreateWorkflow( _   GetType(Workflow1), parameters) C#

var parameters = new Dictionary<string, object>();

15

parameters.Add("ProductID", 1); parameters.Add("Quantity", 10); WorkflowInstance instance = workflowRuntime.CreateWorkflow( typeof(StateMachineDemo1.Workflow1), parameters);

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

Order received Product ID: 1 Quantity: 10

There should then be a five second delay before you see the following output: Output

The item has shipped The order is complete Press any key to continue... Press any key to exit the application.

To see what happens if the item is not in stock, make the following modification to the Main method: Visual Basic

parameters.Add("Quantity", 15) C#

parameters.Add("Quantity", 15);

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

Order received Product ID: 1 Quantity: 15 The item is not in stock The order is canceled Press any key to continue... Press any key to exit the application. Conclusion

In this tutorial, you learned the basic concepts involved in creating and executing a state machine workflow. You saw that the first step in designing a workflow is to get an accurate view of the business process you are modeling. You can then decide whether to build a sequential or state machine workflow. Can you identify discrete states, places where the workflow waits for an action it does not control? If so, you will likely create a state machine workflow. The State, SetState, StateInitialization and StateFinalization activites are specific to state machine workflows. As you saw, you can use those and other activities to build a state machine workflow. Your use of these other activities will usually be the same regardless of the type of workflow you build. In this tutorial, you did not see how a host application raises events. That is obviously an important thing to learn, and you are encouraged to learn about this in a later tutorial in this series.

16

Windows Workflow Tutorial: Introduction to Sequential Workflows Introduction

Windows Workflow Foundation (WF), originally introduced as part of the .NET Framework 3.0 with extensions for Visual Studio 2005’s designers, has continued to be enhanced for the .NET Framework 3.5 and Visual Studio 2008. WF makes it possible, and for many workflow scenarios, even easy to create robust, manageable workflow-based applications. WF is actually many things: It’s a programming model, and runtime engine, and a set of tools that help you create workflow-enabled applications hosted by Windows. (For more information on WF, drop by the portal site). In this series of tutorials, you’ll work through several different examples, showing off various important features of WF, and the corresponding tools and techniques using Visual Studio 2008. Because workflows generally deal with specific business-oriented processes, and these tutorials can’t begin to emulate your specific processes, you’ll find that the specific examples shown here tend to focus on either scenarios that you can control in order to demonstrate the specific workflow features (such as working with files in the file system), or they focus on business processes with “holes” left for your imagination to insert real-world behaviors. This first tutorial introduces the basics of creating Workflow applications, using a console application as the workflow’s host. Along the way, you’ll investigate the various workflow-focused templates that Visual Studio 2008 provides, and you’ll learn what happens when you create and run a workflow application. You’ll try out a few of the many different workflow activities, as well. (These tutorials assume that you have Visual Studio 2008 installed, along with the .NET Framework 3.5. You can choose to work in either Visual Basic or C#--the steps listed here call out specific differences between the languages, when necessary. There’s a lot to cover in this first tutorial, so fire up Visual Studio 2008, and get started! Investigate the Workflow Templates

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 shown in Figure 1.

17

Figure 1. Visual Studio 2008 provides these workflow templates. In general, WF allows you to create two different types of workflows: sequential, and state machine. Sequential workflows provide a structured series of steps in which one activity leads to another, and steps generally occur immediately one after another. A step might wait for some event (an email to arrive, for example), but sequential workflows are often appropriate for tasks that operate without human intervention. State machine workflows provide a set of states; transitions between the states define the behavior. In general, state machine workflows react to events which move the workflow’s current activity from one state to another. Each workflow that you design is simply a class that inherits from one or the other of the System.Workflow.Activities.SequentialWorkflowActivity or System.Workflow.Activities.StateMachineWorkflowActivity classes, and most of the Visual Studio 2008 workflow project templates create a class that inherits from one or the other of these base classes for you. (In addition, each of these templates includes assembly references to the various assemblies required in order to design and run workflows.) If you want to create a sequential workflow, you can select any of the following templates: •

Sequential Workflow Console Application, which includes a Console application host application that helps run your workflow. You’ll investigate this specific template in this tutorial.



Sequential Workflow Library, which creates a .NET library containing only a sequential workflow class. This project template does not include a host application, so you will need to supply your own in order to execute a workflow.



Sharepoint 2007 Sequential Workflow, which creates a sequential workflow suitable for use with SharePoint 2007. (For more information on SharePoint 2007 and workflows, visit this site.)

If you want to create a state machine workflow (the topic of another tutorial in this series), you can select any of the following templates, which correspond to the similarly named templates previously listed: •

SharePoint 2007 State Machine Workflow



State Machine Workflow Console Application



State Machine Workflow Library

In addition, you can select the Empty Workflow Project template to create an empty project, with just the necessary references set; or you can select the Workflow Activity Library template to create a library in which you could create custom workflow activities. Create a Workflow Project

For this demonstration, select the Sequential Workflow Console Application template, name your application WorkflowDemo1, and select an appropriate folder for the project. Click OK to create the project. (Note that every workflow must be hosted by some application—something has to create an instance of the workflow runtime, which, in turn, creates an instance of the workflow you want to execute. In this example, you’ll create a Console application that hosts the workflow.) At this point, the workflow project contains a single workflow class, named Workflow1. This blank template should be opened for you (if not, double-click the corresponding code file in the Solution Explorer window to open it), and the workflow designer is ready for you to add your own activities (see Figure 2).

18

Figure 2. The workflow designer is ready for you to add activities. In the Solution Explorer window, right-click the Workflow1 file, and select View Code from the context menu. You’ll find code like the following, clearly pointing out that the workflow that you’re designing is really just a class that inherits from the SequentialWorkflowActivity class: Visual Basic

Public class Workflow1     Inherits SequentialWorkflowActivity End Class C#

public sealed partial class Workflow1: SequentialWorkflowActivity {   public Workflow1()   {   InitializeComponent();   } }

Just as a Windows Form that you design within Visual Studio becomes an instance of a class that inherits from the System.Windows.Forms.Form class when you run the application, the workflow that you design within Visual Studio becomes an instance of a class that inherits from either the SequentialWorkflowActivity or StateMachineWorkflowActivity class when you run the application. Later, you’ll add code to this partial class in order to add behavior to your workflow. (Actually, there’s more of a similarity than just that—in Visual Studio, when you load a file that contains a class that inherits from the Form class, Visual Studio displays the form using the Windows Forms designer. The same thing happens with a workflow class—when you ask Visual Studio to load a file that contains a class that inherits from one of the two base activity classes, it displays the class using the appropriate workflow designer.) Select View | Designer to return to design view. Look in the Toolbox window (if it’s not visible, select View | Toolbox to display it). You’ll see two tabs that deal with Workflow: the tab labeled Windows Workflow v3.0 contains most of the built-in workflow activities you’ll use, and the tab labeled Windows Workflow v3.5 contains the new workflow activities added in the .NET 19

Framework 3.5 (this tab contains only two activities dealing with WCF and workflow, and you won’t need these items for these tutorials). For now, select the Windows Workflow 3.0 tab, and expand it (see Figure 3).

Figure 3. The Windows Workflow v3.0 tab in the Toolbox window contains most of the built-in workflow activities. Some of the activities you see in the Toolbox window have obvious behavior—the Code activity allows you to execute code, the IfElse activity allows you to make choices, and the While activity executes an activity while some condition remains true. Other activities, such as the SynchronizationScope activity, require a bit more study to determine their purpose. In this tutorial, you’ll work with the simple Code and IfElse activities—later tutorials will walk you through using some of the more complex activities, such as the Replicator, While, Listen, and HandleExternalEvent activities. Add Activities to the Workflow

To get started, from the toolbox, drag a Code activity onto the design surface, so that the designer looks like Figure 4 just before you drop the activity. Once you drop the activity, the designer looks like Figure 5. (Note that as you drag activities onto the designer, you see one or more green “dots” indicating where, on the workflow, you can drop your activity.) 20

Figure 4. Before dropping the activity, you see an icon representing where it will finally appear within the workflow.

Figure 5. After dropping the activity, it appears within the workflow design. At this point, the Code activity indicates an error—you haven’t yet told it what to do when the workflow attempts to execute it! You’ll add the code soon. (Although this tutorial doesn’t walk you through the steps, you should get in the habit of providing meaningful names for your workflow activities—otherwise, it gets difficult to manage all the activities in your workflow. Setting the Name property for each activity is a good habit to get into, but it’s not necessary for this simple workflow.) At runtime, the sequential workflow starts at the green arrow, and executes activities, one at a time, in turn, until it reaches the red circle at the conclusion of the workflow. In your sample workflow, you have included only a single Code activity. Executing the workflow will cause it to run any code that you have placed in the activity’s ExecuteCode event handler. To create the code for your activity, double-click CodeActivity1. This creates an associated procedure, ready for you to edit in the code editor. Modify the procedure, adding the following code: Visual Basic

Console.WriteLine("Hello, World!") C#

Console.WriteLine("Hello, World!");

Select View | Designer, and verify that the error indicator disappears, because you have provided code for the Code activity to run. Select the Code activity, and examine the Properties window (see Figure 6). You’ll see that by adding the event handler, you’ve set the activity’s ExecuteCode property, so that the activity “knows” what to do when it becomes the active activity within the workflow. 21

Figure 6. Adding code for the Code activity sets the activity’s ExecuteCode property. Just to verify that you’ve created a working workflow, save and press Ctrl+F5 to run your project. (If you use any other means to run the project, the console window will disappear immediately, once the message appears on the screen.) After a few seconds, the console window displays your message awaits a key press. So far, you’ve created the world’s simplest working workflow, and it really only proved that workflow works at all—clearly, you would never use WF for a simple application like this. Next, you’ll investigate how WF loads and runs your workflow design. Debugging a Workflow

Although this simple application certainly doesn’t require any debugging capabilities, you’ll often need to step through your workflows. As you might expect, you can easily place a breakpoint in the ExecuteCode handler for a Code activity, and step through the code. But what if you want to debug at a higher level, stepping through activities as they execute? You can, and Visual Studio makes the process feel much like debugging at the code level. In the workflow designer, right-click CodeActivity1. From the context menu, select BreakPoint | Insert Breakpoint. At this point, the designer places a red dot on the activity, indicating that the workflow will drop into Debug mode when it begins to execute the activity (see Figure 7).

22

Figure 7. Set a breakpoint on an activity. Press F5 to start running the application. When the workflow reaches codeActivity1, it pauses execution and highlights the activity in yellow, as shown in Figure 8.

Figure 8. At runtime, Visual Studio stops at the activity’s breakpoint. Select Debug | Step Over (or the corresponding keystroke), and Visual Studio steps to the next activity, executing the previous Code activity’s ExecuteCode procedure. Select Debug | Step Into, and Visual Studio steps into the second Code activity’s ExecuteCode procedure, as you might expect. Visual Studio 2008 makes debugging a workflow no more difficult than debugging any other type of application. (In order to step through your workflow, the startup project within Visual Studio must be a workflow project. That is certainly the case in this simple example, but might not be the case in a “real-world” scenario in which the workflow exists in a library, and the host project is separate. In that case, you’ll need to configure Visual Studio so that it starts the host application when you start debugging.) Investigate the Startup Code

The project template created a simple console application for you, and the project automatically loaded and ran your workflow. It’s important to understand exactly how the project does its work, because you’ll often want to 23

host workflows from other types of applications (from Windows or Web applications, or perhaps from a Windows Service)—in those cases, you’ll need to provide your own code to get the workflow running. To investigate the code that actually does the work starting up your workflow, in the Solution Explorer window, double-click Module1.vb or Program.cs. There, you’ll find the following code: Visual Basic

Class Program Shared WaitHandle As New AutoResetEvent(False)   Shared Sub Main()     Using workflowRuntime As New WorkflowRuntime()       AddHandler workflowRuntime.WorkflowCompleted, _         AddressOf OnWorkflowCompleted       AddHandler workflowRuntime.WorkflowTerminated, _         AddressOf OnWorkflowTerminated       Dim workflowInstance As WorkflowInstance       workflowInstance = _         workflowRuntime.CreateWorkflow(GetType(Workflow1))       workflowInstance.Start()       WaitHandle.WaitOne()     End Using   End Sub   Shared Sub OnWorkflowCompleted(ByVal sender As Object, _     ByVal e As WorkflowCompletedEventArgs)     WaitHandle.Set()   End Sub   Shared Sub OnWorkflowTerminated(ByVal sender As Object, _     ByVal e As WorkflowTerminatedEventArgs)     Console.WriteLine(e.Exception.Message)     WaitHandle.Set()   End Sub End Class C#

class Program {   static void Main(string[] args)   {     using (WorkflowRuntime workflowRuntime = new WorkflowRuntime())

24

{ AutoResetEvent waitHandle = new AutoResetEvent(false);       workflowRuntime.WorkflowCompleted +=         delegate(object sender, WorkflowCompletedEventArgs e)           { waitHandle.Set(); };       workflowRuntime.WorkflowTerminated +=         delegate(object sender, WorkflowTerminatedEventArgs e)         {           Console.WriteLine(e.Exception.Message);           waitHandle.Set();       };       WorkflowInstance instance = workflowRuntime.CreateWorkflow(         typeof(WorkflowDemo1.Workflow1));       instance.Start();       waitHandle.WaitOne();     }   } }

This code, which runs as your application loads, starts by creating a new instance of the WorkflowRuntime class, which provides a execution environment for workflows. Every application you create that hosts one or more workflows must create and instantiate an instance of this class. The WorkflowRuntime instance later creates workflow instances: Visual Basic

Using workflowRuntime As New WorkflowRuntime()   ' Code removed here… End Using C#

using (WorkflowRuntime workflowRuntime =   new WorkflowRuntime()) { // Code removed here… }

On its own, a Console application simply quits when the code in its Main procedure completes. In this case, however, you must keep the application “alive” as long as the workflow is still running. The project template includes an AutoResetEvent variable that helps keep that application running until the workflow has completed: Visual Basic

Shared WaitHandle As New AutoResetEvent(False)

25

C#

AutoResetEvent waitHandle = new AutoResetEvent(false);

The AutoResetEvent class allows threads to communicate with each other by signaling. In this case, the Console application runs in one thread, and the workflow runs in a separate thread. The main thread—in this case, the console application—calls the wait handle’s WaitOne method, which blocks the console application’s thread until the workflow’s thread calls the wait handle’s Set method, which allows the main thread to complete. How does this all happen? Given the WorkflowRuntime instance, the code adds event handlers for the runtime’s WorkflowCompleted and WorkflowTerminated events. The WorkflowCompleted event occurs when the workflow completes normally, and the WorkflowTerminated event occurs if the workflow completes abnormally (because of an unhandled exception, for example): Visual Basic

AddHandler workflowRuntime.WorkflowCompleted, _   AddressOf OnWorkflowCompleted AddHandler workflowRuntime.WorkflowTerminated, _   AddressOf OnWorkflowTerminated ' Later in the class: Shared Sub OnWorkflowCompleted(ByVal sender As Object, _   ByVal e As WorkflowCompletedEventArgs)   WaitHandle.Set() End Sub Shared Sub OnWorkflowTerminated(ByVal sender As Object, _   ByVal e As WorkflowTerminatedEventArgs)   Console.WriteLine(e.Exception.Message)   WaitHandle.Set() End Sub C#

workflowRuntime.WorkflowCompleted +=   delegate(object sender, WorkflowCompletedEventArgs e) {     waitHandle.Set(); }; workflowRuntime.WorkflowTerminated +=   delegate(object sender, WorkflowTerminatedEventArgs e)   {     Console.WriteLine(e.Exception.Message);     waitHandle.Set();   };

When either event occurs, the code calls the wait handle’s Set method, which allows the console application’s thread to continue running (in effect, completing the application and allowing the console window to close). If the workflow terminates abnormally, the WorkflowTerminated event handler writes the exception to the console window before calling the Set method. (This is, as you can probably surmise, not terribly helpful—because the 26

message appears in the window immediately before the window closes, you’ll never actually see the message if the workflow throws an unhandled exception!) You’ve investigated all the code provided in the project template, except the code that actually starts your workflow running. This code appears between the code that sets up the event handlers, and the code that calls the wait handle’s WaitOne method: Visual Basic

Dim workflowInstance As WorkflowInstance workflowInstance = _   workflowRuntime.CreateWorkflow(GetType(Workflow1)) workflowInstance.Start() C#

WorkflowInstance instance = workflowRuntime.CreateWorkflow(   typeof(WorkflowDemo1.Workflow1)); instance.Start();

This code starts by creating a WorkflowInstance object, assigning to it the return value of calling the WorkflowRuntime object’s CreateWorkflow method. By passing the type of the workflow to be created as a parameter to the CreateWorkflow method, the workflow runtime can determine exactly which type of workflow to create. Finally, the code calls the Start method of the workflow instance, which begins executing the workflow at its first activity. (Sequential workflows have an initial activity; state machine workflows have an initial state. It’s a subtly different concept, and you’ll understand it better once you try out a state machine workflow.) Although you’ll probably want to spend the most time fixating on the mechanics of the AutoResetEvent object and the wait handle, please don’t—these features are only necessary for workflows hosted by a Console application, to keep the application running as long as the workflow hasn’t completed. When you host a workflow in any other type of application (a Windows Forms application, for example), you needn’t worry about keeping the application alive. Build a Slightly More Useful Workflow

Although it’s important to work through the obligatory “Hello, World” example as you’ve done, the workflow you built really didn’t do much. For the remainder of this tutorial, you’ll build a workflow application with the following features for backing up files in a folder: •

When the workflow starts up, it creates the required backup folder, if necessary, using a Code activity.



Using a While activity, it loops through all the files in the “from” folder.



Within the activity inside the While activity, a Code activity performs the file copy.



The workflow receives its “from” and “to” folders passed as parameters from the host application.

In setting up this simple workflow, you’ll learn how to use the While activity, and also how to pass parameters to a workflow from the host. In Visual Studio 2008, create a new project, again selecting Sequential Workflow Console Application as the template. Name the project BackupWorkflow. In the workflow designer, drag a Code activity, then a While activity, and finally, another Code activity into the designer. When you’re done, the designer should look like Figure 9. Note that each of the activities displays an 27

error condition: the two Code activities require code to execute, and the While activity requires you to supply a condition so that it can determine when to stop executing.

Figure 9. Create this simple workflow. The While activity allows you to drop a single activity within it (note the “Drop and Activity” prompt inside the activity). What if you want to execute multiple activities in a loop? Although you cannot drop multiple activities inside the While activity, for this very purpose, WF supplies a Sequence activity. The Sequence activity acts as a container for other activities, and as far as the While activity is concerned, it contains only a single activity once you place a Sequence activity inside it. Although this workflow only requires a single activity within the While activity, iif you were to add more than a single activity, you would need the Sequence activity. On the other hand, adding the Sequence activity adds significant overhead to the workflow’s execution—therefore, only add a Sequence activity if you need multiple activities within the While activity. As a “best practice”, consider minimizing the number of activities within the While activity, if at all possible.. Drag a Sequence activity inside the While activity. When you’re done, the workflow should resemble Figure 10.

28

Figure 11. The completed layout should look like this. Configure Code Activities

Double-click codeActivity1, creating the activity’s ExecuteCode handler. At the top of the code file, add the following statement: Visual Basic

Imports System.IO C#

using System.IO;

Outside the procedure you just created, but inside the Workflow1 class, add the following declarations. You’ll use these declarations to keep track of the “from” and “to” folders, as well as the current file and total number of files as you’re copying files: Visual Basic

Private currentFile As Integer Private files As FileInfo() Private _totalFiles As Integer Public Property TotalFiles() As Integer   Get     Return _totalFiles

29

End Get Set(ByVal value As Integer)     _totalFiles = value   End Set End Property Private _toFolder As String Public Property toFolder() As String   Get     Return _toFolder   End Get   Set(ByVal value As String)     _toFolder = value   End Set End Property Private _fromFolder As String Public Property fromFolder() As String   Get     Return _fromFolder   End Get   Set(ByVal value As String)     _fromFolder = value   End Set End Property C#

public string toFolder { get; set; } public string fromFolder { get; set; } public int totalFiles ( get; set; } private int currentFile; private FileInfo[] files;

In the codeActivity1_ExecuteCode procedure, add the following code, which initializes the variables: Visual Basic

currentFile = 0 files = New DirectoryInfo(fromFolder).GetFiles totalFiles = files.Count ' Create the backup folder. Directory.CreateDirectory(toFolder)

30

C#

currentFile = 0; files = new DirectoryInfo(fromFolder).GetFiles(); totalFiles = new DirectoryInfo(fromFolder).GetFiles().Count(); // Create the backup folder: Directory.CreateDirectory(toFolder);

Select View | Designer to switch back to the workflow designer. Double-click codeActivity2, and add the following code to the activity’s ExecuteCode handler. This code retrieves the name of the current file to be copied, copies it to the “to” folder (overwriting existing files), and increments the current file counter: Visual Basic

Dim currentFileName As String = _   Path.GetFileName(files(currentFile).Name) files(currentFile).CopyTo( _   Path.Combine(toFolder, currentFileName), True) currentFile += 1 C#

string currentFileName =   Path.GetFileName(files[currentFile].Name); files[currentFile].CopyTo(   Path.Combine(toFolder, currentFileName), true); currentFile++;

At this point, you’ve set up the Code activities and their ExecuteCode handlers, but you’re not done: You still need to configure the While activity, and you need to pass the fromFolder and toFolder values from the host application. In addition, you need to add code in the host that reports on the results of executing the workflow. Configure the While Activity

The While activity in this workflow needs to execute as many times as there are files in the “from” folder. The code includes the currentFile and totalFiles variables—you simply need to expose that information to the While activity. Select View | Designer. In the designer, select the While activity. 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 11). 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 demonstration, you’ll create the condition in the Properties window.

31

Figure 12. Set up the declarative rule condition. Click the tiny “+” sign to the left of the Condition property, expanding the property. Set the ConditionName property to filesLeft. (Naming the condition allows you to use the same condition in a different place within your workflow.) Select the Expression property, and then select the ellipsis to the right of the property. Enter the condition shown in Figure 12. As you type, note the IntelliSense support. Clearly, the Rule Condition Editor window is able to retrieve information about the properties exposed by your workflow as you type. Although Visual Basic developers can enter the expression using Visual Basic syntax (using the Me keyword instead of this), the editor converts the syntax to C# syntax before it closes. Click OK to close the editor.

Figure 13. Create the While activity’s rule condition. The rule condition specifies that your workflow should loop inside the While activity as long as the currentFile value is less than the total number of files. In effect, you’ve created a simple For loop using the While activity and a little bit of code. (You might be wondering if there’s some way to create a For Each loop using an activity. There is, in fact. The Replicator activity can accomplish this same goal, with less code. You can learn more about that activity in a later tutorial.) Configuring Input Parameters

32

You still need to be able to specify the fromFolder and toFolder values from the host application. Because you’ll often need to pass parameters to a workflow, as you start it up, WF provides a standardized mechanism for passing parameters from the host to a workflow. As you did earlier, open the Module1.vb or Program.cs file in the code editor. Currently, the Main procedure creates the workflow instance using the following code: Visual Basic

workflowInstance = workflowRuntime.CreateWorkflow(GetType(Workflow1)) C#

WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(BackupWorkflow.Workflow1));

In order to pass parameters to the workflow, you must create a generic dictionary, using a String type as the key, and an Object type as the value. Add property name and value pairs to the dictionary, and pass it as a second parameter in the call to the CreateWorkflow method. The workflow runtime engine passes the parameters by name to the workflow as it creates the instance. Because the workflow you created exposes public fromFolder and toFolder properties, you can easily pass these parameters from the host application. To finish your workflow, add the following code to the Main procedure, replacing the existing line of code that calls the CreateWorkflow method (feel free to alter the specific folders to match your own situation.): Visual Basic

Dim parameters As New Dictionary(Of String, Object) parameters.Add("fromFolder", "C:\test") parameters.Add("toFolder", "C:\backup") workflowInstance =   workflowRuntime.CreateWorkflow(GetType(Workflow1), parameters) C#

var parameters = new Dictionary<string, object>(); parameters.Add("fromFolder", @"C:\test"); parameters.Add("toFolder", @"C:\backup"); WorkflowInstance instance =   workflowRuntime.CreateWorkflow(   typeof(BackupWorkflow.Workflow1), parameters);

This technique has its own set of tricks. First of all, the parameter names must match public properties (not fields) in the workflow’s class. If you add an invalid property name to the dictionary, you won’t get a compiletime error—instead, you’ll find out at runtime that your property name is incorrect. In addition, the property names are case-sensitive, even when you’re writing code in Visual Basic. Display Workflow Results

As you’ve seen earlier, the workflow runtime’s WorkflowCompleted executes once the workflow completes successfully. In addition, you’ve seen how to pass parameters to the workflow, using a custom dictionary. You can also retrieve values back from the workflow, in the WorkflowCompleted event handler: the 33

WorkflowCompletedEventArgs that the .NET Runtime passes to this event handler exposes its OutputParameters collection. You can use the name of any public property within the workflow as a string index into this collection to retrieve the value of the property. To verify this behavior, in Module1.vb or Program.cs, modify the OnWorkflowCompleted event handler so that it includes the following code (in C#, the event handler appears at the end of a long line of code, which hooks up the WorkflowCompleted event handler using an anonymous method. You’ll find it easier to add this code if you reformat the existing code so that it looks similar to the WorkflowTerminated event handler): Visual Basic

Console.WriteLine("Copied {0} file(s)", _ e.OutputParameters("TotalFiles")) Console.WriteLine("Press any key to continue...") Console.ReadKey() WaitHandle.Set() C#

Console.WriteLine("Copied {0} file(s)", currentFile); Console.WriteLine("Press any key to continue..."); Console.ReadKey(); waitHandle.Set();

Save and run your project. If you’ve followed the directions carefully, the workflow should copy all the files from the “from” folder to the “to” folder, and you should see the results of the workflow in the Console window. Conclusion

In this tutorial, you’ve learned many of the basic concepts involved in creating and executing workflows, using the Windows Workflow Foundation. Of course, the workflows you created are contrived, and exist simply to demonstrate specific features of WF—you’ll need to consider, as you begin thinking about incorporating WF into your own environment, what kinds of tasks lend themselves to running as workflows. Consider this: because WF supports features like persistence, which allows the workflow to persist its state to a data store (SQL Server, by default), WF is best suited for applications in which you have a long-running task that must survive even if the host machine needs to be rebooted during the task’s execution. For simple applications like you’ve seen here, WF is truly overkill. For enterprise solutions in which you have tasks that might not complete for days, or months, WF provides a perfect solution. Now that you’ve gotten a taste for what you can do using WF, take the time to try out the remaining tutorials in this series, and then start building your own workflows. You’ll be amazed at the power in this rich framework.

34

Related Documents