Windows Workflow Foundation


What you are about to read is the longest chapter of this book. Our goal is to get you up to speed so that you can start creating custom workflow programs with Microsoft Visual Studio and deploy these programs within Microsoft Windows SharePoint Services (WSS) sites. However, there is quite a bit of ground to cover along the way.

The chapter begins with a motivation for the Windows Workflow Foundation (WF). We feel it’s essential for you to gain an understanding of why this platform and its unique new development paradigm help you to create programs that automate business processes. After providing this motivation, we then take you on a brief tour through WF development before we begin discussing how it integrates with WSS.

After discussing the WF as it stands alone, this chapter then dives into a discussion of how WSS integrates with and extends the WF. We first explain why the SharePoint team felt the need to extend the WF by adding a human dimension. We then move on to explain how you actually build workflow programs with the WF that target WSS sites. Any readers who want to start immediately building workflow programs inside Visual Studio should feel free to skip ahead to the section titled “Developing Custom Workflow Templates” later in this chapter.

Reactive Programs

The Windows Workflow Foundation is a new programming framework introduced in .NET 3.0 for creating reactive programs. We begin this chapter by explaining exactly what a reactive program is to motivate you to understand why the WF is helpful in building certain types of applications.

A reactive program typically represents a set of instructions used to capture and automate a specific business process. Examples of real-world problems that are best solved by reactive programs include the processing of an insurance claim or the approval of a document, such as a print advertisement or a quarterly report.

Reactive programs often have a common characteristic. Once they start, they cannot immediately run to completion. Instead, reactive programs run up until a point at which they are forced to pause in the middle of their execution and wait. They must wait for additional input from a human being or for some other external event to occur. For example, a reactive program written to capture an approval process must wait on a business user with managerial responsibilities to review and then approve an item or document. When a reactive program pauses and patiently waits for this critical human interaction, it could be a matter of days or even months until it is able to resume its work.

Reactive programs are often referred to as episodic. This means that they run in episodes- short little bursts of activity that are followed by longer periods of inactivity in which they are waiting for additional input. Reactive programs must also be written so that they are resumable. The reactive program must provide some sort of listening mechanism so that it can wake up and continue its execution at the point where it left off when the desired input arrives.

When a reactive program wakes up and resumes its execution, it experiences another episodic short burst of activity until either the program completes or it reaches another point at which it must go back to sleep and await more input. The life cycle for a reactive program that automates a very simple document approval process might involve two or three episodes and one or two places in its logic where it might need to sleep and then resume its execution. The life cycle for a more complicated reactive program could involve 10 to 20 episodes.

At a high level, developing a reactive program to capture a business process involves managing the state for whatever data are involved and writing the control-of-flow logic. For example, a reactive program for document approval might need to track the document name, approver, approval status, due date, and other pieces of data, such as approver comments. When a user initiates an approval process, the reactive program must somehow send a notification to inform the approver that a document needs to be approved or rejected. Finally, the reactive program needs to provide some sort of mechanism so that it can react to the event when the approver either approves or rejects the document.

If you decide that you want to develop a Windows Forms application to model a reactive program for document approval, you can design your application to track the necessary data each time a user initiates an approval process. Your application can be designed to send an e-mail notification to the approver saying that it is time to review a document for approval. The approver can launch your application, and you can display a form with all of the pending documents awaiting approval or rejection from that user. The code you write for this sort of application requires control-of-flow logic to move each document approval process through its life cycle from one episode to the next.

Due to the episodic nature of a reactive program that automates long-running tasks, such as document approval, it is impractical to merely track its data in memory. Instead, it is necessary to write the data associated with document approvals into some persistent store, such as a database. This is a critical aspect of reactive programs because, without persistence, the approval data would be lost in the face of an application crash or machine reboot.

Let’s now change the scenario from the client to the server. Instead of developing a reactive program as a desktop application, imagine that you need to develop it as a server-side application using the ASP.NET Framework. In one sense, the ASP.NET Framework is ideal for building large applications because it provides features that promote scalability, such as built-in thread pooling and the ability to run in a Web farm environment.

However, there are several downsides to building a reactive program from scratch using the ASP.NET Framework. First, much of the scalability afforded to ASP.NET applications is based on the fact that they don’t maintain state in the Internet Information Services (IIS) worker process from one request to the next. Therefore, the development of a reactive program in ASP.NET requires a design and implementation that constantly writes data for a specific approval process into a persistent store, such as a database, and then pulls these data back into memory on the Web server on a per-request basis.

Also consider what is involved when writing the logic for managing the episodes in an ASP.NET application that automates document approval. You must somehow assign tasks to users and then supply some sort of listener to react to their responses. For example, how would you interact with a human when it is time to approve a document?

One approach to solving this problem is to develop a series of ASP.NET pages to model a reactive program. The first page you write is one that allows a user to initiate the approval process on a specific document. You can design this page so that the user can select a document as well as select the user who can be the approver. After selecting a document and an approver, the user can then click a Start button that sends an e-mail notification to the approver. This e-mail notification can provide a link to a second page that enables the approver to add some comments and then click an Approve or Reject button.

The exercise we are going through here is attempting to design a reactive program that coordinates control-of-flow logic through a series of episodes. Clicking the Start button on the first page takes the approval process through the first episode up until a point of waiting. When the approver clicks the Approve or Reject button on the second page, the approval process runs through another episode until completion or up to another point of waiting. As you might imagine, the control-of-flow logic is going to become more difficult to write and much harder to understand and maintain as it spreads out across more pages. Consider trying to write a reactive program for a complex business process in such a fashion that your logic ends up being spread out across hundreds of ASP.NET pages.

At this point, we can make a general observation: ASP.NET and other similar server-side frameworks achieve scalability and robustness through a stateless and connectionless model. However, this scalability and robustness come at a price because they significantly compromise a developer’s ability to manage state and to express the control-of-flow logic in a natural and maintainable fashion. If we changed our design to create the server-side reactive program as a Web service instead of a Web application, the problem only becomes worse because part of the control-of-flow logic must then be written in the client applications that called this Web service.

Wouldn’t it be great if you could develop a single class definition that could manage the state and provide the control-of-flow logic required for a complete reactive program involving many different episodes and lots of branching? That would make it significantly easier for a developer to write the logic required to automate a real-world business process as well as to review a reactive program written by another developer and be able to understand what’s going on.

It would be even more appealing if you can find a runtime environment that fires event handlers in your reactive program whenever it is time to start the next episode so that you don’t have to design and implement your own listener mechanism. To put the icing on the cake, it would be particularly valuable if this runtime environment could deal with the plumbing details such as persistence, multithreading, asynchronous execution, and request queuing that are required to achieve scalability in server-side applications. The lofty goals described here were the motivation for Microsoft to create the Windows Workflow Foundation.

Windows Framework Foundation Architecture

Microsoft created the Windows Workflow Foundation to provide a development platform and set of developer tools specifically for building reactive programs. In WF terminology, these reactive programs are known as WF programs. It is important to note that WF programs must be created in accordance with a programming model that is mandated by the WF. It is also required that these WF programs run in a special environment known as the WF runtime, which is included as part of the .NET 3.0 Framework.

A WF program is a class definition that is usually designed and implemented to automate a business process, such as online purchasing or document approval. Each time you initiate a WF program, you create a workflow instance. You add fields to the class that represents your WF program to manage the state that must be tracked within each workflow instance. You add control-of-flow logic to your WF program to move each workflow instance through its life cycle from episode to episode until it completes or is terminated.

Throughout the life cycle of a workflow instance are periods of inactivity in which its state must be serialized and persisted out into some form of durable storage, such as a database. As another episode begins, this state must be retrieved so that a workflow instance can be deserialized back to its previous form. A significant benefit of the WF is that the WF runtime deals with the persistence and reloading of workflow instances in a manner that is transparent to your WF program. It’s basically one of the services that you get for free when you use the WF.

Take a moment and consider the value that the WF provides on top of the Common Language Runtime (CLR). The CLR provides the plumbing needed to create objects in memory. However, these CLR objects are bound to the lifetime of the hosting process and disappear once the process has been shut down or recycled. The WF extends the CLR by adding support to persist and rehydrate CLR objects across process boundaries and even across machine boundaries as required within Web farm environments.

Tip 

The version numbers associated with the Microsoft .NET Framework can be fairly confusing. You might assume that the Microsoft .NET 3.0 Framework is an update that replaces the Microsoft .NET 2.0 Framework; however, this is not the case. Instead, the .NET 3.0 Framework is a new layer that sits on top of and extends the .NET 2.0 Framework. The .NET 2.0 Framework provides the CLR and ASP.NET 2.0. The .NET 3.0 Framework provides the WF that is built on top of the CLR. WSS 3.0 depends on the CLR and ASP.NET from the .NET 2.0 Framework as well as the WF from the .NET 3.0 Framework.

It’s also important to keep in mind that the WF does more than just save and reload the state associated with workflow instances. The WF must also provide a way to remember how far each workflow instance progresses within its life cycle. When a certain action occurs that triggers the start of a new episode, the WF runtime must determine not only which workflow instance to load, but also where to resume execution within the control-of-flow logic defined inside the WF program. The WF deals with these scheduling requirements by using atomic executable statements known as activities and the concept of bookmarking.

Activities

Activities are the building blocks that developers use to construct WF programs. You can think of each activity as an atomic and reusable set of instructions that represents a single unit of work. As a developer building WF programs, you can reuse many of the activities from within the Base Activity Library (BAL) included with the WF. As a developer building WF programs that target WSS and Microsoft Office SharePoint Server (MOSS), you can also reuse SharePoint-specific activities that are part of the WSS Activity Library.

An activity is a class that inherits from the Activity class defined within the System.Workflow. ComponentModel assembly inside a namespace of the same name. The Activity class contains an overridable method named Execute. The Execute method is overridden by activity authors to give their activities the behavior they require. Note that the Execute method of an activity is always called by the WF runtime and never by programmers directly.

It’s helpful if you regard activities in the same fashion as you think about server-side controls in ASP.NET. Like controls, activities are black boxes of reusable code that expose properties and events. As with controls, the consumer of an activity can simply drag and drop it on a design surface within Visual Studio. The activity consumer then initializes the properties for the activity by using a standard property sheet. The activity consumer also generates event handlers for activities and writes code behind these event handlers. This development paradigm makes it relatively simple to add and maintain custom logic within a WF program.

The one aspect of an activity that makes it fundamentally different from an ASP.NET control is that it is resumable. The WF programming model builds the capability of resuming execution into each activity through an internal bookmarking scheme that involves delegates and callback methods. When the WF runtime loads a workflow instance into memory to begin another episode, certain activities within the WF program provide possible points of re-entry into the flow of executable logic.

Another very important aspect of the WF programming model is that certain types of activities can have child activities, which are known as composite activities. The WF programming model includes a class named CompositeActivity that inherits from Activity. The CompositeActivity class extends the Activity base class by including an Activities property that exposes a collection used to add and track child activities. Many of the activities you reuse from the BAL inherit from CompositeActivity and can contain child activities.

Each composite activity manages the execution of its child activities, which allows composite activities to provide control-of-flow constructs within a WF program. For example, the BAL provides a composite activity named While that repeatedly executes a child activity as long as some condition within the WF program remains true. There is another composite activity named IfThen that conditionally executes child activities contained in two possible paths depending on whether a certain condition in the WF program is met.

Your ability to express control-of-flow constructs within a WF program using composite activities results in creating application logic that is natural and easy to understand. It also makes it possible to encapsulate and reuse complex control-of-flow constructs in a fashion that isn’t possible in programming languages, such as C# and Visual Basic .NET. For example, the BAL provides a composite activity named Parallel that executes two different groups of child activities in parallel. As a consumer of the Parallel activity, you don’t have to worry about starting two different paths of execution at the same time, nor do you have to worry about the details of synchronizing the completion of these two paths before moving on to the next activity. These details of managing the control of flow are handled by the logic encapsulated within the Parallel activity.

Let us give you one more example. The purpose of a very powerful composite activity named Replicator is to take a flow of one or more child activities and replicate this flow so that it can be executed multiple times with different parameters. For example, imagine that you have created a flow of activities to assign a task to a user who needs to approve a document and then to wait on that user to either approve or reject the document. However, what should you do if you need three different users to approve a document in a particular business scenario? You can add the flow of approval-related activities within the Replicator activity and configure it to execute the flow three different times. You can even use the property sheet to switch the behavior of this replication by changing the ExecutionType property back and forth between Sequence and Parallel.

Creating WF Programs

A WF program is a composite activity that contains child activities. The WF program’s child activities can also be composite activities with children of their own and so on. Therefore, a WF program is really nothing more than a composition of activities with a hierarchical treelike structure.

Every activity within a WF program has a Name property that is assigned by the activity consumer at design time. The Name property for an activity represents important metadata that the WF runtime uses to identify continuation points in the WF program’s logic. Note that the Name property cannot be changed at run time.

Every activity has a Parent property that references the composite activity in which it is contained. The only activity that should have a Parent property value equal to null is the root activity that represents the WF program itself. When it is time to start a workflow instance, the WF runtime calls the Execute method on this root activity of the WF program. The lifetime of the workflow instance is then controlled by however the WF program manages the execution flow from one activity to the next.

The WF programming model provides support for creating two different styles of WF programs. The first style is a sequential WF program that typically has several predicable paths of execution that can be modeled in the form of a flowchart, as shown on the left side of Figure 8-1. When you create a sequential WF program to model a business process, you can commonly use composite activities such as While, IfElse, and Replicator to design the control of flow and achieve the conditional branching required.

image from book
Figure 8-1: In the WF, programs can model sequential workflows or state machine workflows.

In real-world scenarios, certain types of business processes are difficult to model using a flowchart when a business process has many possible paths of execution, as shown on the right side of Figure 8-1. A state machine WF program models the different states that a business process goes through on its way toward completion.

For example, think about modeling the business process used to automate online credit card purchases. This model could define states such as OrderCreated, CreditCardAuthenticated, OrderFullfilled, AwaitingBackorder, and OrderShipped. Once you have designed a state machine WF program as a finite set of states, you then add the required logic by defining the transitions between these states in terms of actions and events. This makes it possible to evolve the logic for a complex business process without having to worry about the linear flow from the beginning to the end.

The WF programming model provides a base class of each type of WF program. The SequentialWorkflowActivity class is used to create sequential WF programs, while the StateMachineWorkflowActivity class is used to create state machine WF programs. Both of these classes inherit from CompositeActivity and enable you to create a derived class that serves as the root activity in a WF program.

Given the scope of this chapter, we’re presenting examples of only sequential WF programs, which will suffice for creating WF programs that automate a business process, such as document approval in a WSS or MOSS environment. However, you should note that both styles of WF program are useful for automating business processes within WSS and MOSS. As you learn more about WF development, you will see that some business processes are easier to model with sequential WF programs, while others are more manageable and easier to model using state machine WF programs.

Visual Studio Workflow Designer

After discussing the concepts of composing a WF program by using activities, let’s see how it’s actually done. The first thing you need to do is install the .NET 3.0 Framework on your development workstation so that the WF runtime and WF assembly libraries are present. Remember that WSS 3.0 depends on the WF. Therefore, if you have already successfully installed WSS 3.0, you know the WF is already present on the machine.

After installing the .NET 3.0 Framework, you must then install Visual Studio Extensions for Windows Workflow Foundation. This is the add-in created by the WF team, and it installs the workflow designer into Visual Studio. Once the Visual Studio add-in has been installed, you should see several new Visual Studio project types, such as Sequential Workflow Library and State Machine Workflow Library, for creating new WF programs. When you begin working with the workflow designer, it is helpful to create small test projects by using the Visual Studio project template named Sequential Workflow Console Application. Doing so makes it possible for you to create a WF program and experience using the workflow designer, as shown in Figure 8-2. The project types also create a Console application that acts as a client test harness. To get a “Hello World” WF program up and going, you can drag a Code activity from the Toolkit onto the WF program designer, add an event handler with a call to Console.WriteLine, and press F5. There’s nothing like instant gratification.

image from book
Figure 8-2: WF programs are created by dragging and dropping activities onto the workflow designer.

We introduce one last concept about activities in this section. The WF programming model allows for activities with data-bound properties. The main idea behind data-bound properties is that they facilitate the declarative flow of data across activities. The implementation involves assigning an expression to an activity’s property value that is evaluated at run time. We will revisit this topic later in the chapter in the section titled “Developing Custom Workflow Templates” when we begin to build WF programs for a document approval process in WSS. You’ll see that data binding is required yet is also very easy to set up inside Visual Studio by using dialog boxes supplied by the workflow designer.

WF Runtime

It’s important to remember that the WF does not supply an application server or any type of hosting process in and of itself. Instead, the WF is a general-purpose runtime environment that can be loaded into the process for any application you would like. Let’s take a look at the code for a simple console application that fires up the WF runtime and then uses a custom .NET class named Workflow1, which represents a WF program, to start a workflow instance.

  using System; using System.Workflow.Runtime; using System.Workflow.Runtime.Hosting; namespace HelloWorkflowConsoleApplication {   class Program {     static void Main() {       // start WF runtime       WorkflowRuntime wfRuntime = new WorkflowRuntime();       wfRuntime.StartRuntime();       // create and start workflow instance       WorkflowInstance workflowInstance1;       workflowInstance1 = wfRuntime.CreateWorkflow(typeof(Workflow1));       workflowInstance1.Start();       // pause until test workflow instance completes       Console.ReadLine();       // stop WF runtime       wfRuntime.StopRuntime();       wfRuntime.Dispose();     }   } } 

The preceding code is fairly easy to understand. A hosting application simply needs to start the runtime, and it can then create and start workflow instances from any WF program. Though this process makes it relatively simple to write and test simple WF programs, this console application doesn’t provide support for one of the most valuable aspects of the WF-the capability to persist the workflow instance into durable storage so that it’s not tied to the lifetime of the hosting process. To add that support, the hosting application needs to initialize the WF runtime with one or more WF runtime services.

An application or service written to use the WF must often interact with an external entity, such as a backend database server, to persist the serialized data for its workflow instances. The WF was designed so that all such interaction with external entities is delegated to the WF runtime services. A key aspect of this design is that each WF runtime service is modeled using an abstract class such that various developers and companies can create their own pluggable versions when required.

Consider a workflow instance that needs to be serialized and written out to a database several times within its lifetime. What type of database should be used? Should the WF force everyone to use the same type of database? Fortunately, the answer is no; any developer or company can create a pluggable component that persists workflow instance state to whatever type of database they would like. They can then create a hosting application that starts up the WF runtime and initializes it to load their component as the WF runtime service that manages workflow instance persistence.

Let’s now discuss a workflow instance that is created to automate a document approval process for a document within a WSS site. When it’s time to persist the serialized data for the workflow instance, where should the data be saved? The obvious answer is to save it right inside the WSS content database. Fortunately, the high-level design of the WF enabled the WSS team members to write their own persistence service to make this possible.

The WF programming model defines an abstract base class named WorkflowPersistenceService that defines a set of abstract methods called by the WF runtime when it’s time to load or save workflow instance state. The WSS team created a class named SPWinOePersistenceService that inherits from WorkflowPersistenceService and implements the methods required to load and save workflow instances from the content database. The WSS runtime also provides the initialization logic required within the IIS worker process (W3WP.EXE) to load and start up the WF runtime and initialize it by using the SPWinOePersistenceService class as a WF runtime service. The big picture of how all of the pieces fit together is shown in Figure 8-3.

image from book
Figure 8-3: The WSS runtime loads the WF runtime environment and initializes it with a custom runtime service to save and load workflow instances from the content database.

In this section, we discussed the architecture of WF runtime services to demonstrate two points. First, WF runtime services provide a flexible, pluggable architecture that makes it possible to store workflow instance data anywhere you choose. Second, the WSS team has taken advantage of this flexible architecture to persist workflow instance data to the content database.

Now that you have a high-level understanding of how these pieces fit together, there really isn’t a need for you to learn more about the details of how WF runtime services work. This is a key reason why the WSS development platform provides value over the ASP.NET development platform. If you are writing an ASP.NET application to host WF programs, you might be required to develop your own WF runtime services and also to worry about the details of starting and initializing the WF runtime with whatever WF runtime services you needed.

This concludes our brief introduction to the WF. We are ready to move ahead and begin discussing the layers that WSS and MOSS add on top of the WF. If you are looking for a good how-to guide as you get started using the Visual Studio workflow designer and learning how to use the Base Activity Library provided by the WF, we recommend you read Microsoft Windows Workflow Foundation Step By Step by Kenn Scribner. If you want to continue building your understanding of how the WF works internally, we recommend that you read Essential Windows Workflow Foundation by Dharma Shukla and Bob Schmidt. This book does a great job of providing a bottom-up view of the WF from the perspective of a software architect and an advanced developer. It also delves into many topics that we do not cover in this book, such as writing your own custom activities and developing state machine WF programs.




Inside Microsoft Windows Sharepoint Services Version 3
Inside Microsoft Windows Sharepoint Services Version 3
ISBN: 735623201
EAN: N/A
Year: 2007
Pages: 92

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net