This documentation is archived and is not being maintained.

Exposing a Declarative WCF Service

Visual Studio 2010

Applies to: Windows Communication Foundation

Published: June 2011

Author: Alex Culp

WF 4.0 makes it very easy to expose a workflow as a service. The Microsoft Visual Studio® 2010 development system includes a template for the WCF workflow service that creates a skeleton of the service for you.

The Delay activity is the single most powerful, prebuilt activity that is available in WF4.0. Use it to solve many business problems. By itself, the Delay activity pauses the workflow until the timer expires. It can be used to perform tasks at a specified time. Its real power becomes evident when it is used in combination with the Pick activity. Used together, it is possible to handle situations that require you to wait for either the Delay activity to fire or for a service call. An example of this situation is when you check out a book from the library. After a couple of weeks the book becomes overdue if it is not returned. The following figure illustrates a Pick activity with two branches. One branch has a trigger named Receive Book. The other has a Delay trigger named Overdue Delay. If the Receive Book activity receives data before the Overdue Delay activity finishes, then the library's records are updated. If the book is not returned within the allotted time, then the library sends an overdue notice.

Sample Workflow that uses Pick and Delay Activities

Referenced Image

In order to develop a complete solution, you would have to build a service that keeps track of all the books in the library that have been checked out, and that sends out notifications when books become overdue. This is not a trivial job.

The following sections describe some of the common uses for declarative WCF services.

Creating Complex Business Processes

As was discussed earlier, as imperative services become increasingly complex, they become increasingly difficult to understand. By choosing a declarative approach, you gain the ability to create a visual representation of complex businesses processes from within the IDE. A business analyst or other stakeholder can see a model of the workflow that looks like the flowcharts they already know how to read.

Creating Stateful Workflow Services

A stateful service is any long-running workflow that may stop to wait for additional input from a service, or wait a specific amount of time before continuing. While the workflow waits, it is persisted to disk (a SQL Server database) to conserve resources. The library example that was discussed in the previous section is a perfect example of a stateful workflow service that can be built with WF 4.0.

Creating Composite Services

A composite service is the aggregation of two or more services. WF 4.0 allows you to orchestrate multiple service calls, and optionally return an aggregate result. This is shown in the following illustration.

Referenced Image

A WF 4.0 composite service is exposed that communicates with three different business services. Clients can communicate either with the composite service, or with the finer-grained business services.

The following sections describe additional technical considerations that you should be aware of before using declarative WCF services.

Receiving Messages in the Workflow Service

After you create a service, the first activity you see is the ReceiveRequest activity, which exposes your workflow as a service. It has properties that allow you to control various elements, such as the name of the service contract and the namespace. What is notably missing is an object browser to bind the receive activity to the service and operation contracts. This is because WF 4.0 is declarative. This means that you do not have the fine degree of control that you may be used to from creating services imperatively. If you must have that control, you will need to write some custom wrapper code that creates a WCF service to make calls to the workflow on the clients' behalf. However, in most situations this is unnecessary because you can expose the workflow service over the same bindings that you use for an imperative web service.

Calling a Service from a Workflow

There are three ways to call a service from a workflow.

Calling a Service by Using "Add Service Reference"

The easiest way to call a service from a workflow is to add a service reference to the service you are planning to call. When you do this from a workflow project, it creates the necessary workflow activites. There are, however, some caveats. First, if you have more than one service reference, and if the two services have an operation with the same name, you cannot use this approach. You must either rename one of the operations, which may not be in your control, or you must create a custom activity to call the service. The other drawback is that there is no straightforward way to share service contracts. When you control both the workflow service and the service you consume, it is convenient to share the contract interface so that changes to the contract are apparent immediately. Unfortunately, you cannot do this with service references. Breaking contract changes are not found until runtime. If you share a contract, breaking changes are found at compile time.

Calling a Service by Using a Send Activity

The Send activity, which is included in WF 4.0, allows you to make service calls to other workflow services, but it is limited in its ability to call WCF services.

Calling a Service by Using a Custom Activity

If you use a custom activity, you can call any service by using either synchronous or asynchronous methods. A custom activity also allows you to share service contracts between the service you are calling, and the workflow you are building. The following code is for a resusable activity that allows you to call a service with a single request parameter, and to get a result parameters as the output.

/// <summary>
/// Calls a service method on a service that takes TInput input and returns a response of TOutput.
/// </summary>
/// <typeparam name="TService">Service interface </typeparam>
/// <typeparam name="TInput"></typeparam>
/// <typeparam name="TOutput"></typeparam>
[Designer(typeof(CallServiceWithResponseActivityDesigner))]
public sealed class CallServiceWithResponseActivity<TService, TInput, TOutput> : CodeActivity where TService:class 
{
    public InArgument<string> EndpointConfigurationName { get; set; }
    public InArgument<string> Operation { get; set; }
    public InArgument<TInput> Input { get; set; }
    public OutArgument<TOutput> Output { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
        var channelFactory = new ChannelFactory<TService>(this.EndpointConfigurationName.Get(context));
        TService service = channelFactory.CreateChannel();
        var serviceCommunicationObject = service as ICommunicationObject;

        try
        {
    
            TOutput response = (TOutput) typeof (TService).InvokeMember(
                    this.Operation.Get(context),
                    BindingFlags.InvokeMethod,
                    null,
                    service,
                    new object[] {this.Input.Get(context)});

            Output.Set(context, response);
                
        }
        catch (Exception ex)
        {
            //Make sure we abort the service instead of closing it
            if (serviceCommunicationObject != null)
            {
                try
                {
                    serviceCommunicationObject.Abort();
                }
                catch (Exception)
                {
                    //eat exception about failing to abort the channel
                }
                    
            }
            throw;
        }

        if (serviceCommunicationObject != null)
        {
            //close the communication channel on success
            serviceCommunicationObject.Close();
        }
    }
}

This code assumes that there is a single request to the service, and a single response from the service. If the operation you are calling needs additional parameters, or does not return any results, you must modify the code.

Correlation

One of the more challenging aspects of writing WF 4.0 services is correlation. Correlation is how the workflow runtime knows which persisted workflow instance matches the service call. If the workflow service contains a single receive, does all the work without additional input, and completes, then correlation is not an issue. However, it is essential for workflows that might have multiple receive activities, bookmarks, or delay activites. One of the most frequent problems occurs when you receive service requests that must be correlated to a persisted service, and the service has moved to another step in the workflow where it is no longer waiting for the service call. When this happens, an exception is raised in the workflow that you must handle. You can simply write the error to a table, or you may have to create a new workflow to handle the situation. This choice depends on your own sense of how important the errors are. For more information about correlation, see "Correlation" on MSDN at http://msdn.microsoft.com/en-us/library/dd456784.aspx.

Performance

Performance is much improved in WF 4.0, but workflows still entail overhead. For more information, see Performance Characteristics of Windows Workflow Foundation on MSDN at http://msdn.microsoft.com/en-us/library/aa973808.aspx.

Monitoring

Monitoring is much easier to configure in WF 4.0. To enable monitoring and persistence, you must first run the schema scripts, and then the logic scripts from the WINDIR%\Microsoft.NET\Framework\v4.xxx\SQL\EN folder. Next, set the connection string appropriately in your web.config file. Persistence and monitoring are now both set up. To make things easier, consider hosting your workflow inside of the Windows Server AppFabric. For more information, see the Windows Server AppFabric web page at http://msdn.microsoft.com/en-us/windowsserver/ee695849. In addition to the monitoring that AppFabric provides for WCF services, there are also some dashboards to monitor the workflow services.

A declarative service that use WF 4.0 is an excellent approach if you must model complex business processes. A declarative service also solves some unique problems that can arise when there are long-running, event-driven processes. WF 4.0 does present some challenges that must be considered during the design phase of your project, when you evaluate whether to use the imperative or the declarative approach for your services.

For more information, see the following resources:

Show: