Duplex Workflow Service Sample

Download sample

This sample shows how to perform asynchronous duplex communication between two communicating services. This sample also shows how to perform localhost-to-workflow communication by using messages. While executing duplex communication between two services, the two services must exchange a context before the communication can occur in both directions. The service that initiates the communication receives the context in the reply to its message. To support the communication from the receiving service to the initiating service, the initiating service must send its context information in the first message.

Note

This sample requires that .NET Framework version 3.5 is installed to build and run. Visual Studio 2008 is required to open the project and solution files.

For more information about setting up this sample, see One-Time Set Up Procedure for the Windows Communication Foundation Samples.

Messages are also used for communication between the localhost and the service. The localhost exposes a well-known endpoint that the service can use to call back. This sample implements a LocalWorkflowServiceHost type that provides the functionality to create the local listener as well as the workflow service host for the hosted service.

Duplex Workflow Service Sample Architecture

This sample includes the following communicating entities:

  • Client host

    The client host communicates with the client workflow by calling operations on IReverseContract. When the client host first calls the BeginWork operation, the client workflow is created. In response to the BeginWork operation, the client workflow sends back a message that includes the context for the client workflow. The channel that the host creates to communicate with the client workflow now has the context for further communication. This sample shows how the client host uses the same channel to send multiple work items to the client workflow.

    For the workflow to communicate with the client host, the client host has a well-known endpoint that it listens on. In this sample, that well-known endpoint is named HostEndPoint. The LocalWorkflowServiceHost is used to create that well-known endpoint for the client host as well as the workflow service host for the client workflow.

    LocalWorkflowServiceHost localHost = new LocalWorkflowServiceHost(typeof(ClientWorkflow),new ClientHost());
    localHost.WorkflowRuntime.WorkflowTerminated += 
    delegate(object sender, WorkflowTerminatedEventArgs e) 
    {
        Console.WriteLine("WorkflowTerminated: " + e.Exception.Message);
    };
    localHost.WorkflowRuntime.WorkflowCompleted += 
    delegate(object sender, WorkflowCompletedEventArgs e) 
    { 
        Console.WriteLine("WorkflowCompleted: " + 
                         e.WorkflowInstance.InstanceId.ToString()); 
    };
    localHost.Open();
    
  • Client workflow (exposed as a service)

    The client workflow implements IReverseContract. The service workflow uses this, as shown in the previous illustration, to communicate with the client workflow. The client workflow first calls the BeginWorkflow operation, which creates the service workflow. The reply returns the context for the service workflow, which is used in subsequent client workflow communications to the service workflow. As part of the BeginWorkflow operation, the client workflow passes its own endpoint reference, which contains the endpoint address as well as the context headers to the service workflow. The service workflow uses this endpoint address to communicate asynchronously with the client workflow.

    The WaitForBeginWork event handler in the initial state implements a code activity named DoSetReturnAddress. This activity sets the endpoint reference, which is sent to the service workflow, as shown in the following sample.

    private void SetReturnAddress(object sender, EventArgs e)
    {
        EndpointAddress epr = 
                  ContextManager.CreateEndpointAddress(ReturnUri, 
                                  this.ReceiveWorkItemComplete);
        ReturnAddress = EndpointAddress10.FromEndpointAddress(epr);
        DebugOutput("[ClientWorkflow:SetReturnAddress] " + 
                              epr.Headers[0].GetValue<string>());
    }
    
  • Service host

    This sample implements a workflow service host. It opens up a listener that listens to requests from the client. The first request from the client creates an instance of the service workflow. All subsequent requests are routed to the same workflow instance because the requests carry the context in the message header.

  • Service workflow (exposed as a service)

    The service workflow implements IForwardContract. The service workflow in the BeginWorkflow operation receives the endpoint reference for the client workflow. The service workflow applies that endpoint reference to the Send activity that sends a message asynchronously to the client workflow. The WaitForBeginWorkflow event handler provides the BeginWorkflow Receive activity. Inside the Receive activity exists a code activity named ApplyReturnAddress, which uses the return address in the Send activity to send a message to the client workflow, as the following code shows.

    private void ApplyReturnAddress(object sender, EventArgs e)
    {
        // apply ReturnAddress to ReverseEndpoint
        EndpointAddress epr = ReturnAddress.ToEndpointAddress();
        ContextManager.ApplyEndpointAddress(
                              this.SendWorkItemComplete, epr);
        DebugOutput("[ServiceWorkflow:ApplyReturnAddress] " + 
                           epr.Headers[0].GetValue<string>());
    }
    

    The LocalWorkflowServiceHost class creates the necessary infrastructure for host-to-workflow communication. The local workflow service performs two main tasks:

    • Creates a local listener for the host to listen to messages from the workflow.

    • Instantiates the WorkflowServiceHost to create a listener for the workflow.

    The class also provides helper functions for the host to maintain the context that is used to communicate with the workflow. If the host recycles, this context is used to communicate with the workflow instance.

The WorkflowServiceUtility project provides all the helper functions that are required to manipulate the context. It provides functions to extract the context from the channel, apply context to a channel, and apply an endpoint address to the Send activity.

To set up, build, and run the sample

  1. To install the persistence providers, run the CreateStores.cmd script located in the One-Time Set Up Procedure for the Windows Communication Foundation Samples topic.

  2. Download the Workflow Service Utilities and save it so that the DuplexWorkflowService and WorkflowServiceUtility folders are located in the same parent folder.

  3. If you do not want to use persistence providers, comment out the <WorkflowRuntime> section from the App.config files.

  4. This sample uses two persistence databases. The service workflow uses the NetFx35Samples_ServiceWorkflowStore, and the client uses the NetFx35Samples_ClientWorkflowStore. You can create these stores in SQL Server or SQL Server Express. The current connection strings located in the App.config files assume that you are using SQL Server Express. If you create the stores in SQL Server, make sure to change the connection strings in the App.config files.

  5. Once the client host and the service host are running, press Y in the console window to process work items. You can send multiple work items for processing. If you shut down and restart the client, it resumes from where it left off.

  6. To test the durable nature of the services, shut down both the client and the service applications, and then restart the client application first followed by the service application. This is for sequencing, because as soon as the service workflow is running, it tries to send messages back to the client; if the client is unavailable, you receive an exception.

  7. On the client, the context is stored in the Client.ctx file. The file is stored in the \bin directory of your sample. When you reopen the client, it checks whether the file is present. If the file is present, it applies the stored context to the channel that is being created. If the workflow service has finished and you open the client with the Client.ctx file still in your \bin directory, it tries to apply the context to the channel. This produces an error because the workflow instance with which you want to communicate is not there. Delete the file and try again.

© 2007 Microsoft Corporation. All rights reserved.