image

The purpose of this chapter is to provide a grand tour of Windows Workflow Foundation (WF). It includes a high-level description of the major features of WF. The chapter also provides some necessary background information that will help you get the most out of the remaining chapters. Because of the summary focus of this chapter, it doesn’t contain much actual workflow code.

The chapter begins with a brief description of the features and capabilities that are included with WF. Following this, I provide an overview of the .NET assemblies and namespaces within them that are related to WF. Topics related to the activity life cycle are then discussed, followed by a short primer on workflow expressions. The chapter concludes with a list of the top features that are currently missing from WF 4.

WF Features and Capabilities

In this section, I briefly describe the most important features and capabilities of WF. The focus is on familiarizing you with the out-of-the-box features that are available when you build your own workflow-enabled applications.

Declarative Activity Model

Activities are the core building blocks of workflows. Each activity is narrowly designed to accomplish some type of useful work. Workflows are composed by assembling multiple activities into a useful pattern to accomplish some goal.

WF uses a declarative model to describe activities and workflows that I think is the single most important feature of WF. Individual activities and workflows are organized as a hierarchical tree of activities. Some activities are leaf nodes; others act as containers for child activities. Using this declarative model, you declare the activities that you want to execute in some prescribed sequence. The sequence and conditions under which the activities execute is based on their location in the tree. Changing the conditions under which an activity executes may be as simple as changing its location in the tree. The declarative model encourages a clear separation between the actual work to be accomplished (represented by the individual activities) and the mechanism that coordinates that work.

This is in contrast with the procedural model. In that model, you need to implement not only the code necessary to accomplish some useful work but also the control code to coordinate that work. Under this model, the lines are often blurred between the code that accomplishes the actual work and the coordinating code. The coordinating code is typically implemented as a series of imperative procedural statements that may be entwined with the code that performs the real work. Changing the sequence or conditions under which the work is executed is accomplished by changing the procedural code.

In WF, activities and workflows are really interchangeable components since they are built upon the same model. There is nothing distinctive about a workflow that prevents it from being used as an activity (a building block) in another workflow. Likewise, there is nothing that prevents you from directly executing a single activity as if it were a much larger workflow.

images Tip The real deciding factor that distinguishes an activity from a workflow is how it is used. If the component is directly executed by an application, I call that a workflow. If it is used as a single building block within a larger workflow, I call that an activity. A single component may serve both roles: sometimes executed directly and other times used within a larger workflow.

Using the same underlying model for activities and workflows provides important advantages. It allows you to freely declare and assemble activities without regard for how they were originally used. When you declare your own custom activities, you don’t need to predestine or restrict their future use based solely on their original use. For example, you might initially declare a workflow and execute it directly to meet your immediate needs. Later, you might discover that you also need to execute the steps in that workflow as part of a much larger workflow. Since an activity is a workflow and a workflow is an activity, you can now include the original workflow as an individual step in the new workflow.

This consistent model also allows you to more easily compose activities. You can start by declaring activities that complete small, granular units of work. You then compose those activities into slightly larger ones that coordinate a handful of activities. Those activities are then incrementally composed into larger activities and so on. Composition is now promoted as a first-class authoring style of this model.

WF always uses a declarative model, regardless of how the model is constructed. Most of the time, you will declare activities in Xaml, an XML-based markup syntax that supports the declaration of objects and properties. However, WF also supports the declaration of activities entirely in code. Even though it may be defined in code, the model is still a declarative one.

Standard Activities

One of the most significant WF deliverables is a standard activity library. This library consists of the most common essential building blocks that you will need to construct your own activities and workflows. Chapter 3 provides a summary of the standard activities that are included with WF.

Custom Activities

Although WF includes a set of standard activities, you are by no means limited to using only the activities that come with WF. In fact, you will find it difficult to harness the real power of WF without developing your own custom activities.

Custom activities can be implemented in procedural code or declaratively composed from other activities. Composition can take place in code or, more often, using Xaml.

WF provides several base classes that you can use to author your own custom activities. Each base class is tailored to a particular type of work. For example, the CodeActivity class provides a simple way to synchronously execute imperative code on the workflow thread. In contrast with this, the AsyncCodeActivity class provides a way to execute work asynchronously on another thread. Here are the major categories of work that you can accomplish within an activity:

  • Atomic unit of work
  • Asynchronous unit of work
  • Long-running unit of work
  • Control flow activities

Activities that are implemented entirely in code are compiled into Common Language Runtime (CLR) types. Activities that are declared in Xaml are either compiled into CLR types or deserialized and executed directly from the file system.

Chapter 3 provides additional information on creating custom activities.

Workflow Designer

WF includes a workflow designer that is integrated into Visual Studio. This designer allows you to visually declare activities and workflows using a familiar drag-and-drop interface. Properties of each activity are set using the Properties window or, in some cases, directly on the design surface. Individual activities are selected from the Visual Studio Toolbox and dropped onto the workflow design canvas. The standard activities that are included with WF as well as any custom activities in referenced assemblies are included in the Toolbox.

Custom Activity Designers and Validation

Properties for each activity may be set using the standard Properties window within Visual Studio. In addition, individual activities may support in-place designers that allow you to set the most frequently used properties directly from the design surface.

Custom activity designers are not restricted to only the activities that are provided with WF. The classes that are needed to support an enhanced design experience are also available for use with your custom activities. Although you aren’t required to create custom designers, doing so may greatly increase the productivity of the developers that use your activities.

You can also add validation rules to your custom activities. The purpose of validation rules is to catch potential errors as early as possible in the development process. For example, you can identify required properties for an activity by simply applying the RequiredArgument attribute to the properties. If a developer uses the activity but neglects to set a value for the required properties, they are immediately alerted to the problem with an error that is shown on the design surface. A build error would be generated if the project was built without setting values for the required properties. You can also implement more elaborate validation logic when it is necessary.

Chapter 15 discusses developing custom designers and adding validation to your activities.

Multiple Modeling Styles

WF includes support for two standard modeling styles out-of-the-box: procedural and flowchart. The modeling style determines how the flow of control between individual activities is modeled. Microsoft may provide other modeling styles in the future (for example, state machine), or you can implement activities that support your own modeling style. However, the two styles that are included with WF are both flexible enough to tackle most workflow tasks.

The procedural modeling style uses familiar programming constructs to control the flow of execution. Standard activities are included that mimic C# branching and looping keywords such as if and while. You control the flow of execution by placing child activities into the appropriate control activity. In contrast with this, the flowchart modeling style uses direct links between activities to control the flow of execution. The procedural modeling style could be thought of as rigid, structured, and precise, while the flowchart style is free-form. The two styles peacefully coexist and can be freely mixed within a single activity or workflow.

Workflow Debugger

The Visual Studio debugger provides an integrated solution for debugging your activities and workflows. Breakpoints can be set directly on the workflow designer surface or within a Xaml document for individual activities. Once the debugger stops at a breakpoint, you can step into or stop over activities, just as you would when you are debugging C# code. You can also seamlessly step into code from the workflow model.

Workflow Services

Workflow services are Windows Communication Foundation (WCF) services that are declaratively implemented as workflows. WCF clients directly access the workflow via one or more exposed endpoints. The workflow service is completely self-contained, defining the service contract as well as other parameters within the workflow itself. Common message exchange patterns of request/response, one-way, and duplex are supported.

WF also provides standard activities that allow you to consume WCF services from within an activity or workflow. This allows a workflow to take on the role of a WCF client and invoke services that may or may not be implemented as workflows.

Multiple WCF service operations can be implemented by a workflow. At least one operation must be designated as one that can create a new instance of the workflow. Other operations may work with an existing workflow instance. To route WCF messages to an existing workflow instance, correlation is used. WF supports two types of correlation:

  • Context correlation uses tokens passed within the SOAP headers to identify the workflow instance to receive the message.
  • Content correlation uses one or more data elements from the message itself to route the message.

Chapters 9 and 10 discuss workflow services in further detail.

Multiple Workflow Hosts

All workflows are executed by a workflow runtime that is provided with WF (although it might be more appropriate to call it an activity runtime). Your application code doesn’t directly execute an activity – that task is reserved for the workflow runtime. The runtime is responsible for the execution of individual activities that have been scheduled for execution, thread management, persistence, tracking, bookmark processing, and so on.

Activities can be hosted within your application using one of the workflow host classes provided with WF. These classes are your public interface to the workflow runtime. The WorkflowInvoker class is the simplest way to execute an activity. It is designed to execute an activity with the simplicity of calling a C# method. The WorkflowApplication class is a full-featured workflow host that supports additional features such as asynchronous operation, persistence, and bookmark support for long-running workflows.

You also have several choices for hosting workflow services. They can be hosted in a Microsoft-provided environment such as IIS, or they can be self-hosted within your own application. The WorkflowServiceHost class is used when you want to self-host workflow services.

WF also provides the ability to directly use a Xaml file containing an activity definition without any intermediate build step. A workflow service Xamlx file can be loaded and used directly from the file system in a similar way. Once an activity definition has been deserialized, it can be hosted by any of the self-hosting classes in the same way as a definition that was compiled into a CLR type.

Chapter 4 further discusses the workflow hosting options. Chapter 9 covers hosting for workflow services.

Workflow Extensions

WF supports an extension mechanism that can provide additional functionality for activities and workflows. These custom workflow extensions are added to the host classes described in the previous section. Once an extension is added, the public methods of the extension can be invoked from a custom activity. A singleton instance of an extension can be shared by multiple workflow instances, or a new instance can be constructed for each workflow instance.

Workflow extensions are first demonstrated in Chapter 8.

Persistence

Workflow persistence is the ability to save and later reload the state of a workflow instance. This capability is important since it facilitates the development of long-running workflows that may take minutes, hours, or days to complete. While a workflow is waiting for some external input, its state is safely persisted, and it can be unloaded from memory. When the external input is available, the instance is reloaded into memory, and processing can continue.

WF includes support for persistence to a SQL Server database. This SQL Server support should meet your needs for most workflow applications. However, persistence is a pluggable component. You can use the SQL Server support, or you can develop your own custom class (an instance store) that supports persistence to some other medium. Custom workflow extensions can also participate in persistence, allowing you to inject additional data that is persisted along with the workflow instance.

Chapters 11 and 12 discuss workflow persistence.

Bookmark Processing

WF supports the ability to temporarily suspend execution of a workflow and resume execution at a later time. The general mechanism that supports this is called bookmark processing. Just as a real bookmark identifies your place in a real book, a WF bookmark identifies the point of execution in a workflow.

When a bookmark is created, the workflow signals that it is waiting for some external input. At this point, the activity yields control over the workflow thread, allowing it to be used elsewhere in the same workflow. If there is no other scheduled work to be performed within the workflow, the workflow becomes idle and is capable of being persisted and unloaded. When a bookmark is resumed, execution continues at the location of the bookmark. Additional data can be passed into the workflow instance when a bookmark is resumed.

This same general mechanism is used for conventional workflows and also for workflow services. In the case of workflow services, the receipt of a WCF message triggers the resumption of a bookmark.

Bookmark processing is first demonstrated in Chapter 8.

Expressions

One of the design goals of WF was to support a completely declarative design-time experience. This included the ability to set values for activity properties and workflow variables. What WF really required was a general-purpose mechanism to declaratively execute some code and return a value. The mechanism that was chosen was workflow expressions. In the broadest sense, expressions in WF include any activities that return a single value. Expressions allow you to declare code that assigns a value to a property, saves a value to a workflow variable, and so on. You can think of expressions as the small bits of glue code within the declarative model.

At design time, expressions are entered within the designer using the property editor or a separate expression editor dialog. They become part of the workflow model and are saved as part of the definition document (Xaml or Xamlx).

Expressions are discussed in more detail later in this chapter and are used throughout this book.

Transaction Support

Transactions allow you to ensure consistency when you are performing update operations against a resource such as a SQL Server database. WF provides the ability to declare a transaction within the workflow model using the TransactionScope activity. Child activities that are added to this activity all share the same transaction, and all succeed or fail as one unit of work. If you are using workflow services, you also have the ability to flow an existing external transaction into the workflow.

Chapter 13 covers transaction support. Chapter 10 discusses flowing a transaction into a workflow service.

Compensation and Exception Handling

Unhandled exceptions within an activity or workflow can be handled by the host application. WF also provides a declarative way to handle exceptions internally within a workflow. Handling the exception locally provides you with a better opportunity to possibly recover from the failure.

Compensation is the undoing of work that has already completed. WF provides a mechanism to optionally declare a set of activities to execute when compensation is triggered.

Chapter 13 covers compensation and exception handling.

Workflow Tracking

Workflow tracking is a built-in mechanism that provides automatic instrumentation of workflows. WF provides a fairly large amount of potential tracking information for each workflow. The tracking data includes the time when each activity was scheduled for execution, when it began execution, when it ended, each transition to a different status, and so on. You can define and apply a tracking profile to filter the information to just the right amount to meet your needs.

The most important feature of workflow tracking is that it is automatic. It is built into the workflow runtime infrastructure and can be enabled without changing your workflows at all.

Out of the box, WF provides a tracking participant that directs tracking data to the Event Tracing for Windows system (ETW). This data is viewable using the Windows Event Viewer. If you want to persist the tracking data in a more usable form, you can develop your own tracking participant.

Chapter 14 covers workflow tracking.

Designer Rehosting

The workflow designer that is integrated with Visual Studio allows developers to efficiently declare activities and workflows. WF provides the classes necessary to host this same designer within your applications. This is especially important when your application end users require the ability to customize the workflows that are used by the application. If this is the case, you might want to rehost the designer to provide them with the same highly productive environment that developers enjoy. The rehosted designer can also be useful in situations where you want to monitor the progress of running workflows.

Chapter 17 discusses rehosting the workflow designer.

WF 3.x Migration

WF 4 is not the first workflow framework produced by Microsoft. WF 3.0 was first introduced in 2007 and subsequently enhanced with WF 3.5 in 2008. If you have already invested in WF 3.x, rest assured that your investment has not been wasted. Visual Studio 2010 and .NET 4 includes support for the original 3.x version of WF. However, the 3.x WF components have been built to use the new 4 CLR. This enables continued development and maintenance of your 3.x workflow applications in a 4 CLR. In addition, WF 4 includes an Interop activity that allows you to execute your 3.x custom activities with the 4 runtime. You can use this mechanism as a migration tool when you decide to move to the new 4 environment.

Chapter 18 discusses migration strategies and 3.x interop.

Assemblies and Namespaces

In this section of the chapter, I outline the assemblies and namespaces within them that are related to WF. This information should provide you with a general road map to where you might find a particular class.

System.Activities

This assembly contains the most frequently used WF types, including the standard activities, support for VB expressions, classes for hosting workflows, workflow tracking, and so on. Here are the most important namespaces that can be found in this assembly:

image

System.Activities.DurableInstancing

This assembly contains classes that are related to workflow persistence. It has only a single namespace and doesn’t contain a large number of classes, but it is very important if you need to use SQL Server persistence.

image

System.Runtime.DurableInstancing

This assembly isn’t strictly defined as a WF assembly, but I’m including it since it contains classes that are vital for workflow persistence. It contains the base classes that allow multiple persistence mechanisms (instance stores) to be developed. The SqlWorklfowInstanceStore class (from the System.Activities.DurableInstancing assembly) uses the base classes provided in this assembly.

image

System.Activities.Presentation

This assembly includes the classes that you will use to implement custom activity designers or to rehost the workflow designer.

image

System.ServiceModel.Activities

This assembly contains the classes that support workflow services and WCF integration.

image

3.x Assemblies

In addition to the assemblies that support WF 4, .NET 4 also includes the assemblies that support the previous versions of WF (3.0 and 3.5). These assemblies have a 4 version number and are built to use the .NET 4 runtime, but they contain the classes that were originally developed for versions 3.0 and 3.5.

I wanted to draw your attention to these assemblies for two reasons:

  • To identify the assemblies that you will continue to use if you are developing or maintaining an application that uses the 3.x version of WF.
  • To warn you to avoid referencing these assemblies if your intent is to build new applications using WF 4. Many of the class names between the 3.x and 4 versions of WF are the same or similar (although the namespaces are different). Referencing the wrong version of a class will likely result in a great deal of anguish and frustration.

I won’t detail every namespace in these assemblies, since the 3.x version of WF is not the focus of this book. Here are the assemblies:

image

Activity Life Cycle

In this section, I briefly present topics that are related to the WF runtime environment. These topics provide background information that should be helpful as you work through the remaining chapters of this book.

Definition vs. Runtime Instance

The WF runtime makes a clear distinction between an activity definition and an activity instance. As a developer, you declare and maintain the activity definition. The definition is the declarative tree of activities that you modify with the workflow designer and that is saved as an Xaml file (or for workflow services a Xamlx file). The file containing the definition may be compiled into a CLR type, or it may be deserialized and used directly from the file system.

images Note Although I’m using the term activity here, this also applies to a workflow that is composed of other activities. Remember that activities and workflows share the same model and are really different names for the same thing.

To instruct the workflow runtime to execute a particular activity, you pass it an instance of that activity’s definition. But the instance of the definition that you construct isn’t executed by the runtime. It is simply a template or guide to the runtime that defines the activity’s structure. Using the activity definition as a guide, the workflow runtime creates an instance of the root activity and executes it. If that activity is a container for other child activities, the root activity schedules execution of each child activity and so on.

As each activity is executed, the runtime creates an instance of the ActivityInstance class for it. This internal structure is used to track the execution state of the activity as it is executed. It also maintains the variables and arguments that are in scope for the activity’s execution. Once execution of an activity has been completed, the ActivityInstance that was acting as the runtime wrapper for the activity is no longer needed. The runtime places the ActivityInstance back into a pool of these objects for later reuse.

As you develop your own custom activities in code, you will frequently use the ActivityContext class (or more likely one of the classes that derive from it such as CodeActivityContext, AsyncCodeActivityContext, or NativeActivityContext). For example, in a custom activity that is derived from the base CodeActivity class, an instance of the CodeActivityContext is passed as an argument to the Execute method. This object is the developer’s view into the ActivityInstance runtime structure. This explains why the context object is passed to the Get method of each argument or variable to retrieve its value. The context is needed to obtain the value that is currently in scope for the current execution of the activity.

images Note Chapter 3 dicusses the use of the activity context, the Execute method and other topics related to authoring custom activities. Chapter 16 covers more advanced custom activity topics.

Definition vs. Runtime Variables

If an activity is executed multiple times (perhaps as the child of a looping activity such as the While activity), a single instance of the activity is created. However, a separate ActivityInstance is used for each execution through the loop. This runtime behavior has a significant impact on private variables that you define within a custom activity. If the activity defines a private variable using a CLR type, that variable becomes part of the activity definition. In this case, the value of the CLR variable is maintained between executions since there is a single instance of the variable. For example, an activity might include a counter variable that you increment each time the activity is executed. If the counter variable is defined as a CLR type (Int32), the ending value of the counter will contain the expected execution count.

On the other hand, you may choose to define private variables within an activity using the Variable<T> class that is provided with WF. This type of variable defines storage for a single runtime instance of the activity that is maintained by the ActivityInstance wrapper. The ActivityContext object must be used to retrieve the variable value that is currently in scope for each execution of the activity. Since a new ActivityInstance is used each time an activity is executed, the activity receives a fresh starting value of a Variable<T> with each execution. If the same counter variable was defined as a Variable<Int32>, the ending value would likely not be what you expect. Instead of an ending value containing the total execution count, it would likely end with a value of one (assuming that the variable begins with a value of zero each time the activity is executed).

images Warning Be aware of the differences in behavior between CLR variables and Variable<T> when you implement custom activities in code.

Activity States

As activities are executed, the workflow runtime maintains their execution state. Here are the known activity states as defined by the ActivityInstanceState enum:

  • Executing
  • Closed
  • Canceled
  • Faulted

All activities begin in the Executing state. When they have completed all of their work, they normally transition to the Closed state, which means that they successfully completed their work. If an activity has scheduled the execution of any child activities, it remains in the Executing state until all children have transitioned to the Closed state. An activity also remains in the Executing state while it is waiting for the resumption of a bookmark, if it has been persisted, or if it is unloaded from memory. An activity in the Closed state cannot be resumed.

If cancellation of an activity has been requested (either by its parent activity or directly by the host application), the activity transitions to the Canceled state. Canceling an activity gracefully stops execution and allows any cancellation and compensation handlers to execute. Once it is canceled, execution of an activity cannot be resumed.

There are several ways that an activity can move to the Faulted state. Once an activity is in the Faulted state, it cannot be resumed. If an unhandled exception is thrown, the activity may move to the Faulted state. This depends on how the application handles the exception. If you are using the WorkflowApplication class to host the activity, you specify how exceptions are handled by assigning code to the OnUnhandledException delegate. The value that you return from this code determines how the unhandled exception is ultimately handled. If you return a value of UnhandledExceptionAction.Terminate, the activity transitions to the Faulted state.

If you are hosting a workflow service, you determine how unhandled exceptions are handled using the WorkflowUnhandledExceptionBehavior class. Setting the Action property of this behavior to a value of WorkflowUnhandledExceptionAction.Terminate will terminate the activity and cause it to transition to the Faulted state.

You can also manually terminate a running activity instance, which causes it to transition to the Faulted state. This can be accomplished using the Terminate method of the WorkflowApplication class. You can terminate workflow services by invoking the Terminate method of the WorkflowControlEndpoint.

Please refer to Chapters 9 and 13 for more information on exception handling. Chapter 13 also discusses compensation handlers.

Expressions

As I mentioned earlier in the chapter, expressions are the small bits of glue code within the declarative model. They are pervasive within WF. Most of the time, you use expressions to set activity properties (arguments really) or workflow variables. Expressions come in two flavors:

  • Visual Basic expressions
  • Expression activities

The Visual Basic expressions are what you will use most often since they are supported within the workflow designer. Expression activities are a set of individual activities that are included with WF. They are designed to be added individually to the workflow model or chained together with other expression activities to return a result. They are primarily used when you compose an activity in code from other activities.

Visual Basic Expressions

As you have already seen, expressions that you enter at design time use the Visual Basic (VB) syntax. This may seem strange, especially if you create C# projects. But Microsoft has indicated that it was easier to implement expressions in VB because it was able to leverage existing components that parse and interpret the expressions. Microsoft has promised that VB is the first language dialect that is supported for expressions, but it won’t be the last. Ideally, it will offer a Visual Studio add-on that supports C# expressions.

VB expressions are represented by the VisualBasicValue<TResult> and VisualBasicReference<TResult> classes (found in the Microsoft.VisualBasic.Activities namespace). These are two very special expression activity classes. They accept a string expression as input (using VB syntax of course) and return the designated generic type. But when expressions are entered via the Properties window or the expression editor, they are serialized to the Xaml (or Xamlx) file within square brackets. The square brackets are shorthand for VisualBasicValue<TResult> or VisualBasicReference<T>.

For example, in one of the examples from Chapter 1, you set the WriteLine.Text property to this:

String.Format("Hello {0} Welcome to Workflow!", ArgFirstName)

This is a VB expression that returns a string. If you take a look at the Xaml file for that example, you should see that the expression was serialized with square brackets like this:

<WriteLine
   Text="[String.Format(&quot;Hello {0} Welcome to Workflow!&quot;, ArgFirstName)]" />

However, there are some exceptions to this square-brackets rule. If the expression that you enter is a simple primitive literal (Int32, Boolean, Double, and so on) or a string, it is serialized as a literal value without the brackets. For example, for the first Hello Workflow example in Chapter 1, you entered the literal "Hello Workflow" (including double quotes) for the WriteLine.Text property. This was serialized to Xaml like this:

<WriteLine Text="Hello Workflow" />

When this string literal is processed at runtime, it is wrapped in an instance of the Literal<TResult> class. This class is one of the expression activity classes that are provided with WF.

VB Primer for Workflow Developers

Since VB is the only language option for expressions (for now), you need to familiarize yourself with the VB syntax. In this section, I review a few VB language constructs and keywords and contrast them with C#.

My intent is not to teach you to become a productive VB developer. I think you probably need an entirely different book if that is your goal. Instead, I’ll try to focus on the very small subset of the language that you are most likely to use when you write workflow expressions. If you need to use some VB feature that I don’t cover, your best bet is to head to the online MSDN documentation. And feel free to skip this section if are already intimately familiar with VB.

A Few Basic Rules

First, VB is not case sensitive. So whether you enter And, and, or AND, the VB expression should produce the same result. VB generally requires that you use a line continuation character (an underscore) if you need to continue a statement on another line. I say generally because the latest version of VB should be able to properly detect a statement continuation if the line break is at an unambiguous point. If the expression editor complains about a multiline VB statement, just add an underscore to the end of the prior line.

Many of the operators and keywords are the same (or very similar) between C# and VB. However, there are a few areas that are drastically different. One area that greatly differs between C# and VB is generic types. For example, in C# you might create an instance of a generic dictionary like this:

new Dictionary<string, int>()

But in VB, you need to use the Of keyword to create the same dictionary like this:

New Dictionary(Of string, int)

Note that the VB code does not require the parentheses "()" to execute the constructor. They are optional.

Another area that is very different between C# and VB is the use of the object initializer syntax. This syntax allows you to set property values during the construction of the object. For example, assume that you have a class defined like this in C#:

public class MyType
{
    public string Prop1 { get; set; }
    public int Prop2 { get; set; }
}

If you want to create an instance of this type and initialize the properties using C#, you might code something like this:

new MyType { Prop1 = "foo", Prop2 = 123 };

To initialize the same object in VB, you use the With keyword like this:

New MyType With { .Prop1 = "foo", .Prop2 = 123 }

Notice that each property name is also preceded by a period.

Operator and Keyword Comparison

In this table, I list some commonly used C# operators and keywords along with their VB equivalents:

image

Data Type Comparison

This table lists some commonly used data types for C# and VB:

image

Literal Data Types

This table lists the literal type characters that are used for C# and VB. These are the characters that you can optionally place after a numeric literal to force the data type of the literal.

image

Expression Activities

Most of the time you will use the built-in support for VB expressions. They are the easiest way to supply an expression where one is required. WF also includes a set of expression activity classes that you can use when you are composing an activity in code. These activities have designer support, so you won’t find them in the Visual Studio Toolbox.

images Note These expression activities are available for your use, but they are not the recommended way to enter expressions. I’ve covering them here to make you aware of their existence, not to recommend them above VB expressions.

These activities are located in the System.Activities.Expressions namespace. Here is a representative selection of these activities. This is not an exhaustive list of all expression activities, but it should be enough to give you a feel for the type of activities that are provided:

image

All of these activities derive from the CodeActivity<TResult> activity, and as such they return a single value. This fits the definition of an expression that I gave earlier. As you can tell from the list of activities, these are primitive building blocks that you can use directly or combine with other activities to create more complex expressions. They are all generics, so you will need to provide one or more generic types when you use each activity. For example, the Add expression is defined as Add<TLeft,TRight,TResult>.

Using an Expression Activity

The most common way to use these activities is to include them during the composition of a custom code activity. For example, in this short example, you will implement a simple custom activity that adds two numbers. It doesn’t add any real value over and above the Add activity that it uses, but it does illustrate how these expression activities might be used.

Create a Workflow Console application, and name it ExpressionActivites. You can remove the Workflow1.xaml file since it won’t be used. Add a new Code Activity to the project, and name it AddNumber. Here is the code for this activity:

using System;
using System.Activities;
using System.Activities.Expressions;
using System.Activities.Statements;

namespace ExpressionActivities
{
    public sealed class AddNumbers : Activity<Int32>
    {
        public InArgument<Int32> NumberOne { get; set; }
        public InArgument<Int32> NumberTwo { get; set; }

        protected override Func<Activity> Implementation
        {
            get { return () => BuildImplementation(); }
        }

        private Activity BuildImplementation()
        {
            return new Sequence
            {
                Activities =
                {
                    new Assign
                    {
                        To = new OutArgument<Int32>(ac =>
                            this.Result.Get(ac)),
                        Value = new InArgument<Int32>
                        {
                            Expression = new Add<Int32,Int32,Int32>
                            {
                                Left = new InArgument<Int32>(ac =>
                                    NumberOne.Get(ac)),
                                Right = new InArgument<Int32>(ac =>
                                    NumberTwo.Get(ac)),
                            }
                        }
                    }
                }
            };
        }
    }
}

images Note Chapter 3 provides much more information on implementing and composing your own custom activities. Included in that chapter is another example of composition in code.

Here is the code that you need to add to the Program.cs file to execute this test:

using System;
using System.Activities;
using System.Activities.Expressions;
using System.Collections.Generic;

namespace ExpressionActivities
{
    class Program
    {
        static void Main(string[] args)
        {
            Direct();
            Composition();

            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }

        private static Int32 Composition()
        {
            Int32 result = WorkflowInvoker.Invoke<Int32>(
                new AddNumbers(),
                new Dictionary<String, Object>
                {
                    {"NumberOne", 1},
                    {"NumberTwo", 2}
                });

            Console.WriteLine("Result: {0}", result);
            return result;
        }
        private static Int32 Direct()
        {
            Int32 result = WorkflowInvoker.Invoke<Int32>(
                new Add<Int32, Int32, Int32>(),
                new Dictionary<String, Object>
                {
                    {"Left", 1},
                    {"Right", 2}
                });
            Console.WriteLine("Result: {0}", result);
            return result;
        }
    }
}

This code illustrates two ways that you can use one of these expression activities. First, it executes the Add<TLeft,TRight,TResult> expression directly using the WorkflowInvoker class. After all, it is an activity, so there’s really no reason that you can’t execute it directly like this. Second, it executes the custom AddNumbers activity that demonstrates how to use the expression activity during composition of a custom activity. Here are my results:


Result: 3

Result: 3

Press any key to exit

You are not limited to the set of expression activities that are provided with WF. You can always develop your own custom activities that meet your specialized needs. And if those activities derive from CodeActivity<TResult> and return a single value, they can be used as expressions.

Missing 4 Features

Version 4 represents a completely new version of WF. It is a complete redesign and rewrite from the previous versions (3.0 and 3.5). Depending on how you look at things, you could view this version in a couple of different ways.

  • First, since it was completely redesigned and rewritten, you could think of it as version 1.0. In this case, you would expect that it is still rough around the edges and has a few missing features.
  • Second, since it is the third version of WF, you could think of it as the mystical third version where Microsoft finally gets things just right.

The reality is somewhere in between the two extremes. In most ways, I consider it the mystical third version of WF that Microsoft got right. It attempts to correct most of the problems with the previous versions of WF, indicating that the Microsoft designers did indeed learn from past versions (and past mistakes). However, although the core architecture of WF 4 is greatly improved over previous versions, there are missing features. In this respect, it is more or less a 1.0 release.

The following are the most important features that I think WF 4 really should have but are not currently available. No doubt, Microsoft is already working on many of these features. As you begin to work with WF, you will likely develop your own list of features that you’d like to have. In the following short sections, I present my top list (in no particular order).

State Machine

The 3.x version of WF supports a state machine workflow type. A state machine represents a different way of thinking about a problem. Instead of attacking a problem procedurally by defining steps to execute, you define the states that can exist for a workflow. A workflow instance can exist in only one state at a time. Then you define operations that can be performed while the workflow is in each state. Finally, you define transitions between each state.

Microsoft has released guidance documents that describe how you can mimic the feel of a state machine using the flowchart modeling style in WF 4. But this is really a short-term solution. WF 4 needs a real state machine since some problems can more naturally be modeled with one. Microsoft is working on state machine support and has already delievered the first CTP of state machine activities as a post-4 add-on.

Reuse of WCF Contracts

WF 4 provides a fairly easy way to declare workflow services. These services are exposed as WCF endpoints and can be consumed by any WCF client. A core concept of WCF is the use of contracts. In traditional WCF development, you first define a contract using a C# interface. That interface identifies the service operations (methods) that are supported by your service, along with the arguments and return value (if any) for each operation. The interface is decorated with attributes that identify it as a WCF service contract and also affect characteristics of the service. You then develop a service class that implements the interface. Traditional WCF development definitely emphasizes a contract-first approach.

Although WF 4 eases the declaration of new services, it isn’t built around the concept of using an explicit service contract. It is certainly not a contract-first approach to service development. In fact, the WCF contract that is generated for a workflow service is implied based on the properties and parameters that you set within the workflow. And there is currently no facility in WF to import and reuse WCF service contracts that you have already defined using C# interfaces.

Being a contract-first kind of developer myself, I feel that WF 4 could be improved if it supported these features:

  • The ability to reference and import WCF contracts that are already defined. This might take the form of a utility that generates custom messaging activities based on a properly decorated C# interface.
  • The ability to generate the source for a C# interface based on the implied WCF contract. The source should be autogenerated each time the implied contract changes. The generated interface could then be consumed by client applications that use a channel factory instead of adding service references.

C# Expression Support

WF 4 currently supports expressions entered in Visual Basic (VB) only. According to Microsoft, this limitation was necessary because of development time constraints. Although this is a workable solution, it is a bit awkward to create new C# projects but have to use VB for expressions. A much more consistent development environment would result if expressions were entered in the selected project language. And that includes C#. The expectation is that Microsoft will, in time, provide support for additional expressions languages.

Tracking to SQL Server

WF 4 includes a very flexible mechanism for workflow instrumentation. Workflow tracking automatically provides data that allows you to monitor the progress and performance of individual workflow instances. Out of the box, WF includes a tracking participant that persists the tracking data to the Event Tracing for Windows (ETW) subsystem. Although this works, it isn’t always the optimal solution.

WF 3.x has its own version of a tracking system, and its default persistence medium is a SQL Server database. Although I think ETW tracking has its uses, it would be beneficial to have an out-of-the-box option to persist tracking data to a database. However, tracking to a database is available if you use AppFabric to manage your server environment.

Rules Engine

WF 3.x supports a general-purpose rules engine. The rules engine allows you to make assertions or statements of fact about your data. The rules are organized into a rule set and are executed one at a time against your data. WF 3.x supports forward-chaining of rules, meaning that a rule might be executed more than once if it was detected that data referenced by a rule had changed. There is no native equivalent support in WF 4. However, the 3.x rules engine can be executed using the Interop activity or from a custom activity.

Dynamic Updates

The 3.x version of WF supports the ability to dynamically update a workflow instance after it was started. When the workflow instance is in an idle state, you can add, change, or delete individual activities within the workflow. This update mechanism can potentially be used to help with versioning of long-running workflows. WF 4 does not have any support for dynamic updates to an active workflow instance.

Summary

The focus of this chapter was a brief summary of WF and coverage of a few topics that provide background information. The chapter began with a review of the major features and capabilities of WF. This was followed by a summary of the assemblies and namespaces that you will use for WF development. Topics related to the activity life cycle, activity states, workflow definitions vs. runtime instances, and differences in variable types were covered next.

WF uses VB as the language that you use when entering expressions. After an overview of expressions, the chapter included a short primer on VB. The goal was not to teach you how to develop applications in VB. Instead, it was intended to acquaint you with the elements of VB that you will most commonly use as a workflow developer. The chapter concluded with my list of the top features that are currently missing from WF 4.

In the next chapter, you will learn more about the most basic building block of WF: the activity.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.137.58