Workflow Services – Using Message Contracts

Overview

When building workflow services, very often we need to leverage pre-existing contracts (service contracts, message contracts and data contracts). In the current version of Workflow Services (WF4) we can reuse data contracts and message contracts. In vNext (as Ron Jacobs demonstrated in PDC2010), we will be able to reuse Service Contracts too. This post will show how to reuse Message Contracts in a workflow service.

Scenario

In this scenario, we are implementing a CustomerService. This service expects to receive a “GetCustomers” request message and returns a “GetCustomerResponse”. The flow itself is very simple and of no special significance. It merely serves to demonstrate that the elements of the message contract can be interrogated and that responses can be constructed any way we choose. The workflow service does not constrain us in any way. When defining the consumer proxy however, we must be careful to choose the “Always Generate Message Contracts” option.

Service Implementation

Solution Overview

The following screenshot shows the layout of the solution:

clip_image002_2_0C7AB340

Solution Items

The main items in the solution are the in the Customer Service project:

  • Contracts.cs : Contains the message contracts
  • CustomerFactory.cs : Static class used to construct a GetCustomerResponse

Defining the Message Contracts

In order to define the Message Contracts, we use the attributes [MessageContract], [MessageHeader] and [MessageBodyMember]. The following code snippet shows the definition of the contracts.

 [MessageContract]
public class GetCustomer
{
    [MessageHeader]
    public int CustomerId;
 
    [MessageBodyMember]
    public string CustomerName;
}
 
[MessageContract]
public class GetCustomerResponse
{
    [MessageHeader]
    public int CustomerId;
 
    [MessageBodyMember]
    public string CustomerName;
 
    [MessageBodyMember]
    public string CustomerAddress;
}

Importing the Message Contracts

The service uses a ReceiveAndSendReply pair of activities. To use the message contracts in the content definition of the receive and send shapes we first need to define variables of those types. The following screenshot shows the declaration of the variables.

clip_image004_2_5E8D6087

In the Receive shape, define the content by choosing the “Message” option as shown in the following screenshot.

clip_image006_2_5E8D6087

Similarly, In the send shape, define the content by choosing the “Message” option as shown in the following screenshot.

clip_image008_2_5E8D6087

The following XAML Snippets show the definition of the receive and send activities.

Receive Activity:

 <Receive x:Name="__ReferenceID0" CanCreateInstance="True"
         DisplayName="ReceiveRequest"
         sap:VirtualizedContainerService.HintSize="508.666666666667,88"
         OperationName="GetData"
         ServiceContractName="p:ICustomerService">
    <Receive.CorrelationInitializers>
        <RequestReplyCorrelationInitializer CorrelationHandle="[handle]" />
    </Receive.CorrelationInitializers>
    <ReceiveMessageContent DeclaredMessageType="c:GetCustomer">
        <p1:OutArgument x:TypeArguments="c:GetCustomer">
            [input]
        </p1:OutArgument>
    </ReceiveMessageContent>
</Receive>

Send Activity:

 <SendReply Request="{x:Reference __ReferenceID0}"
           DisplayName="SendResponse"
           sap:VirtualizedContainerService.HintSize="508.666666666667,88">
    <SendMessageContent DeclaredMessageType="c:GetCustomerResponse">
        <p1:InArgument x:TypeArguments="c:GetCustomerResponse">
            [output]
        </p1:InArgument>
    </SendMessageContent>
</SendReply>

Service Logic

As indicated earlier, there is no special significance to the logic in the service

The service works as follows:

  • It examines the ID of the Customer and decides whether it is a “v1 Customer” or a “v2 Customer” category.
  • It then passes this category to a static ‘factory’ class which creates a customer response message.
  • This factory created customer response is returned to the caller.

Consumer Implementation

The sample solution uses an ordinary Console application. We generate the proxy by using the “Add Service Reference” menu. It is important to choose the option to “Always generate message contracts” when configuring the service (use the “Advanced” settings dialog) as shown in the following screenshot.

clip_image010_2_5E8D6087

Summary

The scenario above shows the basics of using Message Contracts. As we have seen in the sample, we can access all the elements of the incoming request including those defined with the [MessageHeader] attribute. This gives us the facility to be able to parse SOAP headers and make decisions based on the values if required.

Written by Santosh Benjamin

“It is with great sadness that this post is being made. After this article was written, our colleague and friend, Santosh ‘Benjy’ Muthiah has passed away. The article has been posted in his memory as Benjy was an avid contributor to the technical community, both external and internal to Microsoft. He was well liked and respected by those who were fortunate enough to work with him and he will be sadly missed by us all. Our thoughts remain with his family.”