Share via

May 2010

Volume 25 Number 05

Workflow Services - Visual Design of Workflows with WCF and WF 4

By Leon Welicki | May 2010

Developers are increasingly adopting service-oriented architecture (SOA) as a way of building distributed applications. For the uninitiated, designing and implementing service-oriented distributed apps can be intimidating. However, the Microsoft .NET Framework 4 makes it easier than ever to implement Windows Communication Foundation (WCF) services using Windows Workflow Foundation (WF).

WCF workflow services provide a productive environment for authoring long-running, durable operations or services where enforcing an application protocol via ordering of operations is important. Workflow services are implemented using WF activities that can make use of WCF for sending and receiving data.

In this article, I will explain how to combine several features of WCF and WF that were introduced in the .NET Framework 4 to model a long-running, durable and instrumented mortgage-approval process for a real estate company, without having to write code. This article is not intended to be a general introduction to WCF or WF, nor does it walk you through the entire process of creating a working solution. Instead, I’m going to focus on the use of important new .NET Framework 4 features through a practical business scenario. A full working solution is included in the code download for this article.

The Scenario

Let’s start by outlining the scenario around which the workflow application was built. Contoso Housing is a real estate company that sells houses and condos. To provide better customer service and an end-to-end buying experience, Contoso partners with three mortgage companies that assist potential customers with their mortgage needs. Each mortgage company offers different interest rates. Contoso prioritizes mortgage vendors by their interest rates to ensure that customers get the best deal (using the assumption that a better rate makes the house more likely to sell).

Customers provide their mortgage request data via a Web application. Each customer enters a customer ID, the price of the house, the amount of down payment, the loan period in years, salary information and some background verification information.

The first step in the application workflow (see Figure 1) uses the customer-entered data to screen the customer and determine eligibility prior to sending the request on to the mortgage vendors.

Figure 1 The Mortgage Approval Process
Figure 1 The Mortgage Approval Process

The application follows these rules:

  • If the applicant had a foreclosure, a bankruptcy or is in a lawsuit, the petition will be rejected.
  • If the applicant does not have credit history and provides a down payment less than 20 percent, the application is returned for revision (incorrect petition), but is not rejected. The customer must provide at least a 20 percent down payment to continue.
  • If none of the above apply, the petition is approved.

The mortgage vendors are contacted according to Contoso’s preferred order to petition the mortgage request. If a mortgage vendor rejects the applicant, the next one is asked. There’s a standard service contract for mortgage approval requests that all vendors implement. During this stage in the process, customers should be able to query the state of their request.

Once the mortgage petition is resolved (one vendor accepted or all rejected), the customer interaction is recorded in Contoso’s customer relationship management (CRM) system through a service exposed by the CRM system. The result is returned to the customer.

Note that the mortgage approval process shown in Figure 1 provides a general description of the business process, but does not have any indication about how to implement it. The workflow service will be an implementation of this process.

Creating a Declarative Service

The workflow service will receive data from the customer, do the initial screening, coordinate the conversation with the mortgage vendors, register the interaction in the CRM service and expose the result to the customer.

The service will be long-running (it can take days or months to complete), durable (it can save the state and resume later), and instrumented (both developers and users will be able to know what’s going on without having to debug the service). By using WCF and WF, you can achieve all this declaratively without writing any code—you just have to assemble and configure components provided by the .NET Framework. Figure 2 shows an architectural diagram of the solution.

Figure 2 Architecture of the Solution
Figure 2 Architecture of the Solution

A WCF workflow service is contained in a .xamlx file that defines the workflow. You can define business processes visually using the WF designer, and the service contract is inferred based on the structure of the workflow.

To create a workflow service in Visual Studio 2010, create a new project, then select the WCF Workflow Service Application template. This project template creates a very simple (but running) workflow with Receive and SendReply activities. This is similar to a class with a method that receives an integer and returns a string. You can extend the workflow by adding more activities to it. These activities might add execution logic or service operations.

To define the service contract, you use the messaging activities provided by WF. The configuration for the service is stored in the web.config file, just like in regular WCF services. If you open web.config, you’ll see a very clean configuration file because WCF workflow services take advantage of the service configuration improvements introduced in WCF 4.

Messaging activities combine WF and WCF in a seamless way. They’re designed to support message-oriented workflows and provide better integration of messaging into workflows. Messaging activities enable workflows to send data out to other systems (Send, SendReply and SendAndReceiveReply), and to receive data from other systems (Receive, ReceiveReply and ReceiveAndSendReply). They also include activities for working with correlation (InitializeCorrelation, CorrelationScope) and transactions (TransactedReceiveScope).

Receive and Send activities allow modeling the messaging interactions within a workflow. The contract in a service can be defined by configuring the Receive and Send activities in it. Each Receive is exposed as an operation. Each Send activity sends a message to a service. The target service does not have to utilize WCF or even the .NET Framework because you interact with it through standard protocols.

Send and Receive can be configured to use either a message-based or parameter-based (RPC) approach for receiving and sending the actual data.  This controls the wire-format in which the data is sent or received.

Describing Processes in Flowcharts

You can model the process using a Sequence, but a quick look at Figure 1 shows that at some point the process requires looping back to a previous step, and that’s not directly supported in sequential workflows. (It would require modeling the loop manually using constructs such as a While activity with a carefully modeled condition.) A Flowchart is a better fit for modeling this scenario.

Flowchart is a new control-flow activity introduced in WF 4 that lets you describe your processes much as you would on a whiteboard. Flowchart describes processes that are sequential in nature, with a single path of execution that may require looping back to a previous step under certain circumstances. To describe the process, the Flowchart uses arrows and boxes, a popular approach among many disciplines.

The default workflow created by the WCF Workflow Service project template is a Sequence, but this does not mean it’s the only control-flow activity you can use in workflow services. To use a Flowchart (or any other composite activity) as the root activity in a service, simply delete the Sequence and add a Flowchart.

Figure 3 shows how easily a Flowchart can be created from the diagrams that business users typically draft to describe a process.

Figure 3 The Mortgage Approval Process as a Flowchart
Figure 3 The Mortgage Approval Process as a Flowchart

Flowchart is a great tool for describing business processes, minimizing the mismatch between executable processes and the way they’re specified. In this case, the Flowchart definition is the documentation of the process.

But let’s slow down a bit. We’ve jumped from an empty Flowchart to a complete one in no time. Let’s go back to the empty Flowchart and start working on it.

The potential home buyer introduces his data through a Web application. This data is passed to the workflow through a Receive activity. The result of including a Receive activity in the Flowchart is that the WorkflowServiceHost exposes an endpoint that allows users to communicate with the workflow by sending messages to it.

As I mentioned earlier, you can configure Send and Receive activities to use either a message-based or RPC-based approach. In this case I configured the Receive activity to use a RPC-based approach. This means it receives a set of input arguments, similar to a method call. To make them available to the workflow, I need to create variables and bind them to the parameters (see Figure 4). Another possible approach would be having a DataContract that collapses all these fields.

Figure 4 Configuring Input Parameters
Figure 4 Configuring Input Parameters

If you want to create and start a new instance of the workflow upon receiving a message, you need to set the CanCreateInstance property in the Receive activity to true. This means the WorkflowServiceHost will create a new instance when it receives a message from this endpoint.

Modeling the Screening Step with WF Composition

Once you have data in the workflow, you can start working with it. The first step is screening, which means verifying a set of conditions in order to determine whether the applicant is eligible for a mortgage before contacting the vendors.

One approach is to add several decision shapes (FlowDecision) to the main Flowchart. This works, but makes the overall process hard to read. Furthermore, any modifications to the screening rules would require updating the main flow. Flowchart seems to be a good fit for expressing conditions visually, but we want to keep the main process lean.

A solution is to add a new Flowchart inside the existing Flowchart. WF 4 has strong support for composition at its core, so activities can be freely composed. This means you can add a new Flowchart wherever you need it, including inside an existing Flowchart. Moreover, the composition is arbitrary and doesn’t impose any limits. You can combine existing activities at your convenience.

The child flowchart is displayed collapsed in the parent (the Flowchart designer does not support expand-in-place). You will need to double-click it to model the screening logic. The screening Flowchart (see Figure 5) is a child of the main Flowchart and has access to its variables and arguments.

Figure 5 Adding the Screening Flowchart
Figure 5 Adding the Screening Flowchart

What if you want to write some code? You could certainly write code to describe the screening process. In this case, for example, you could author a CodeActivity that receives the data from the customer as input, performs the verification (a set of chained if statements in your language of choice), and returns the result. This has its own pro and cons. It offers potentially better performance (all verifications are executed within a single pulse of execution) and more compact representation than the declarative approach. On the other hand, you lose the visual representation of the 
process (opacity), and changing the process requires modifying the code and recompiling.

Sending Results to the Customer

When the screening verification is complete, I need to return the result to the applicant. I received the applicant data through a Receive activity at the beginning of the workflow. To send the reply back I use a SendReply activity (a SendReply for an existing Receive can be created by right-clicking on the Receive and selecting Create SendReply). The combination of Receive and SendReply allows implementing the request-response message exchange pattern. SendReply is configured to send back the result of the operation and a description message to the customer.

Why SendReply and not Send? You can use a pair of Receive and Send activities to model duplex messaging exchange patterns like “request and wait for a response” (similar to a callback), but SendReply is better suited to modeling request-response message exchange patterns (similar to a method invocation).

Deciding Where to Go Next

When the screening is complete, the workflow can continue with the mortgage petition process. There are two branches at this point: Reject and Approved, but when the applicant needs to provide more data, the workflow must go back to a prior step. Flowchart allows modeling this drawing-a-line action to the step where the workflow will proceed.

To decide the path to be taken based on the result of the screening, I use a FlowSwitch activity (similar to a switch statement), as shown in Figure 6.

Figure 6 Each Outbound Arrow in the FlowSwitch Represents a Case in the Switch
Figure 6 Each Outbound Arrow in the FlowSwitch Represents a Case in the Switch


When the mortgage request is deemed incorrect, the workflow asks the customer to provide additional data to an existing workflow instance. How can the Receive activity know that the data it’s receiving is a correction of the data the customer provided earlier? In other words, how can you send another message to a running instance of a workflow? The answer is correlation.

WF 4 introduces a framework for correlation. A correlation is actually one of two things:

  • A way of grouping messages together. A classic example of this is sessions in WCF, or even more simply the relationship between a request message and its reply. 
  • A way of mapping a piece of data to a workflow service instance.

There are several types of correlation available in the .NET Framework 4. I use content-based correlation in this example workflow. A content-based correlation takes data from the incoming message and maps it to an existing instance. Content-based correlation is used when a workflow service has multiple methods that are accessed by a single client and a piece of data in the exchanged messages identifies the desired instance.

To set up the correlation, I declare a variable of type CorrelationHandle. This is the handle used to store the correlation information (customerCorrelationHandle in the companion solution). The next step is using the correlation handle. The Receive activity’s property grid has a Correlations section to configure correlation. It has properties for configuring a correlation handle (CorrelatesWith), for specifying the data that you correlate on through a correlation query (CorrelatesOn) and for initializing a correlation handler (CorrelationInitializers). I configured the CorrelatesWith and CorrelatesOn arguments in the Receive activity to correlate on the customerCode field.

CorrelatesOn and CorrelatesWith can be confusing. Here’s a rule that can help you: a Receive CorrelatesWith an existing correlation handle and CorrelatesOn data specified by a correlation query.

All this can be set up using the WF designer.

I want to correlate on the customer identification so I create a correlation query that extracts the customerCode from the message. Note that correlation queries are authored using XPath, but you don’t need to know XPath because the WF designer creates the query by inspecting the contract of the received message for you.

The first Receive activity in the workflow is configured to create a new workflow instance upon receiving a message. However, it won’t create a new instance when I loop back to it because it’s also configured to correlate on the customerCode. Before creating a new instance, it looks for an existing instance with that correlation key.

Asking Vendors for Rates

If the petition passes the screening, it’s sent to the vendors for aluation. Remember that Contoso works with three vendors and has a preferred order for doing business with them. Therefore the process asks vendors in sequential order until one approves.

All vendors implement a standard service contract for requesting mortgage approval. The only difference between the interest rate requests to the three mortgage vendors is the URI of the service. However, the vendor interest rate request is complicated by the fact that it involves sending a message and waiting asynchronously for a response while simultaneously responding to customer requests for status.

I could model this process once and copy it three times in the workflow, but this would result in lots of unnecessary duplication and thus a serious maintainability issue. I want to abstract this step so I can use the same process multiple times. Ideally, I would provide the workflow with some input data and get a result when it’s done, and that’s exactly what custom activities are for.

WF 4 provides two approaches for creating custom activities:

  • Declarative create a new activity by composing other existing activities.
  • Imperative create a new activity by writing code. There are several base classes with different capabilities that can be derived, including CodeActivity (simple imperative behavior), AsyncCodeActivity (do work asynchronously) and NativeActivity (interact with the WF runtime).

Activities have arguments that define their public signatures in terms of what data they can receive (InArgument) and what data they will return (OutArgument) when they complete. My activity will receive customer mortgage information and a service URI as input, and will return an interest rate and result string message.

I use the Activity item template in Visual Studio to create a new activity declaratively using the WF designer. The designer allows you to author custom activities visually by dragging and dropping existing activities and setting their arguments. The result of authoring a custom activity with the designer is a XAML file that defines an x:Class.

I called the activity AskVendor. The AskVendor activity receives mortgage information from the customer and the URI of the service as input, and provides an interest rate and a result string message as output. If the rate is 0, the petition has been rejected.

AskVendor sends a message to a mortgage vendor asking for an interest rate and waits for a response from the vendor. The response may take minutes, hours or even days to arrive. While waiting for the response, the applicant may want to know the state of the process. Therefore, the activity also responds to status-request messages from the applicant.

To process both actions simultaneously I use a Parallel activity as the root of the custom activity. In one branch I have all the activities for communicating with a mortgage vendor, and in the other the activities to listen for the customer while waiting for the vendor’s response. All messaging activities used within AskVendor are configured to correlate on the customerCode field. Figure 7 shows AskVendor custom activity.

Figure 7 AskVendor Custom Activity
Figure 7 AskVendor Custom Activity

As mentioned, the root of the custom activity is a Parallel. The left branch sends a message to a vendor and waits for a response. Once the response is received, it formats the resulting string message and sets the completed flag to true. The right branch listens for state-query requests from the applicant. The Receive and SendReply are inside of a While activity that’s executed until the completed flag is true. The Parallel activity’s completion condition (executed when a branch is completed) sets the completed flag to true. Therefore, when the left branch completes (the message from the vendor is received), the completed variable is signaled as true and the While at the right is also completed.

Custom activities are just like any other activity. When you create a custom activity it shows up automatically in the activities toolbox. And using it is no different from using any other existing activity: drag it from the toolbox, drop it in the designer and configure its arguments. Because I haven’t created a designer for this activity, the default designer is assigned to it (a rectangle where you can set the DisplayName property). The property grid automatically displays all the arguments of the activity.

I mentioned earlier the strong support for composition in WF 4. This also applies to the custom activities. If you create your own composite activity, you’ll be able to freely compose it with any other existing activity like Sequence, Flowchart, Parallel or even other custom activities.

Invoking the CRM service

Contoso’s CRM system exposes its core functionality as services. One of these services allows registering an interaction with a customer. I could invoke it using a Send activity, but this would imply configuring the activity manually and importing its service data contracts.

It would be great if I could just import the service into WF and execute it. This is exactly what Add Service Reference in a workflow service project does: given the contract of a service, it automatically creates proxy activities (one for each operation in the service) that can be used to invoke the service (see Figure 8). In this case, the CRM service contract has three operations so “Add service reference” has created three activities, which are displayed in the toolbox.

Figure 8 Adding a Service Reference to the CRM Service
Figure 8 Adding a Service Reference to the CRM Service

Communicating the Result

Finally, I need to provide the results to the customer. To keep the app simple, I just use a ReceiveAndSendReply activity that exposes the result to the customer. Once the customer reads the result, the workflow completes.

To do this, I need to drop a ReceiveAndSendReply in the Flowchart. Note that when you drop the activity in the designer surface, you get a collapsed Sequence. This is because ReceiveAndSendReply is an activity template, which means that it’s a preconfigured set of activities (in this case a Sequence with a Receive and a SendReply). You saw this earlier when I added the child Flowchart for screening.

To configure ReceiveAndSendReply, I need to drill in and set the endpoint information in the Receive activity. I also need to configure Receive to correlate by customer identification so that, when the customer sends a message with his customerCode, he will get a response.

Long-Running Work

Once the workflow sends an approval request to a vendor, the service instance will be sitting idle waiting for a response. The response can come in minutes, hours, days or even weeks. This poses some interesting challenges. You probably don’t want to keep all instances in memory because that would consume unnecessary system resources and wouldn’t scale. And if the host process crashes (or in a less apocalyptic scenario, needs to be shut down for maintenance), any unfinished instances would be lost.

Wouldn’t it be great if, when an instance is not doing any work, you could just save it to durable storage and remove it from memory? You can—via the WF persistence framework, which allows saving a workflow instance to a storage medium to be retrieved later.

This means instances are not tied to any existing process or machine. In the example workflow, the screening can occur in one process, asking the first vendor for a rate can occur in another, and receiving the response can occur in a third—without affecting the workflow instance’s execution or its data. This achieves better use of resources, improving scalability and providing resilience—a crash in the host does not produce the loss of the active instances because they can be resumed from the point at which they were last persisted.

Workflow instances are saved to an instance store. WF 4 includes a SQL Server-based instance store, and the persistence framework is extensible so you can write your own. (The PurchaseProcess sample in the SDK shows how to write a very simple text file instance store, for example.)

I use the built-in SQL Server instance store to persist instances of the Contoso workflow. The good news is that you don’t need to write any code to use it. In workflow services, persistence is a behavior that can be configured in the web.config file like so:

<!--Set up SQL Instance Store-->
<sqlWorkflowInstanceStore connectionString="Data Source=.\SQLExpress;Initial Catalog=InstanceStore;
Integrated Security=True;Asynchronous Processing=True"/>
<!--Set the TimeToUnload to 0 to force the WF to be unloaded. To have a durable delay, the workflow needs to be unloaded-->
<workflowIdle timeToUnload="0"/>

The first line configures the persistence behavior to use the SQL Server instance store. The second line instructs the persistence framework to persist and unload instances as soon as they become idle (this means that when you execute a Receive and become idle waiting for a response, the workflow instance will be unloaded and saved in the database).

If a workflow is configured to be persistent and has correlation configured, the host (WorkflowServiceHost) is responsible for loading the correct instance when a message arrives based on the correlation information.

 Suppose you have an instance that’s configured to correlate on customerCode, say for customerCode = 43. You ask a mortgage vendor for a rate and the instance is persisted while waiting for the response (persistence includes saving correlation information). When the mortgage company sends back a message for customerCode = 43, WorkflowServiceHost automatically loads that instance from the instance store and dispatches the message.

Note that the SQL instance store is not installed by default. You need to explicitly install it by running a set of scripts provided with the .NET Framework 4.

Tracking the Service

So I have a long-running service that communicates with other services in a message-based fashion. The service is configured to be durable, does some of its work in parallel, and can execute asynchronous activities. This seems pretty complex. What if something goes wrong? How can you tell which activity failed? What if you just want to know what is going on with an instance of the service?

Visual Studio allows debugging workflows (and you can do step debugging in the WF designer by setting breakpoints, just as you do in code), but this is not an option in production environments.

Instead, WF includes a rich tracking infrastructure that provides data about running workflows. Tracking tells you about things that happen in the workflow (tracking events) to a tracking participant that saves these events. Tracking profiles allow filtering the events that a tracking participant receives so it can get just the information it needs.

WF 4 provides a tracking participant that saves data in the Windows Event Log (EtwTrackingParticipant). You can create your own tracking participants by extending TrackingParticipant. For this workflow I used the default EtwTrackingParticipant. You don’t need to write any code to use it; just provide proper configuration in the web.config file. I start by configuring the service to use the EtwTrackingParticipant:

<!--Set up ETW tracking -->
<etwTracking profileName="HealthMonitoring "/>

I also set it to use a HealthMonitoring profile that provides events that will help to assess the health of our service (see Figure 9). Now the service provides information on events that help monitor its health and fix problems as they appear. The SDK supplies several samples that show how to create your own tracking participant and how to write a troubleshooting profile.

Figure 9 Specifying the Tracking Participant Profile

    <!--The health monitoring profile queries for workflow instance level records and for workflow activity fault propagation records-->
      <workflow activityDefinitionId="*">
              <state name="Started"/>
              <state name="Completed"/>
              <state name="Aborted"/>
              <state name="UnhandledException"/>
            faultSourceActivityName ="*" 

Deploying and Consuming the Service

So I’ve created a workflow using the designer and configured it to use persistence and tracking. The only remaining task is to host and run it.

While developing, you can host the service with the built-in Web service host in Visual Studio 2010. To do this, you just need to run the project with the service and you are done.

Hosting the service in a production environment is only slightly more complex. You can host WCF workflow services in IIS or Azure application server extensions. With Visual Studio 2010, you can create a package that can be directly imported to IIS. If you decide to use Azure, you’ll be able to take advantage of features like the dashboard, which provides summarized information about instances of your service, and query the recorded tracking.

The final step is to actually use the service. In this scenario, Contoso wanted a Web-based interface to allow users to interact with the service. This means consuming the service from an ASP.NET application.

The example WCF workflow service you’ve seen here is just like any other WCF service you can write in plain code. To consume it, you need to add a service reference in the client project. This service reference creates the client proxies to invoke the service.

With the reference in hand you can invoke the service. The client for this service has one operation for each Receive activity in the service. Figure 10 shows the code used to request a mortgage approval (and therefore start a new instance of the service).

Figure 10 Consuming the Service

protected void OnSubmit(object sender, EventArgs e) {
  using (ContosoRealEstate.ContosoRealEstateClient client = 
    new ContosoRealEstate.ContosoRealEstateClient()) {

    string message = "";        
    string result = client.EvaluateMortgage(
      out message,
    lblMessage.CssClass= result;
    lblMessage.Text = message + " (" + result + ")";

    this.btnSubmit.Visible = result.Equals("Incorrect");
    this.btnMonitor.Visible = result.Equals("Approved");

Closing Notes

As you’ve seen, the .NET Framework 4 provides a rich feature set that can be used to build complex real-world solutions by assembling existing components. The framework also provides extensibility points to tailor these components to specific needs that accommodate a wide variety of scenarios.

WCF workflow services let you describe a long-running, durable, instrumented process declaratively by simply composing existing activities and configuring the service—though you can also write your own code if necessary.

In this article, I combined several features in WF 4 and WCF 4 to build a service that does some work on its own and coordinates conversations with other existing services. I built the entire service without writing code, including a new artifact (a custom activity).

There’s a lot more you can do with these .NET Framework 4 technologies.

Leon Welicki is a program manager in the WF team at Microsoft focusing on Activities and the WF runtime. Prior to joining Microsoft, he worked as lead architect and dev manager for a large Spanish telecom company and as external associate professor on the graduate computer science faculty at the Pontifical University of Salamanca at Madrid.

Thanks to the following technical experts for reviewing this article: Dave Cliffe, Vikram Desai, Kavita Kamani and Bob Schmidt