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:
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.
In the Receive shape, define the content by choosing the “Message” option as shown in the following screenshot.
Similarly, In the send shape, define the content by choosing the “Message” option as shown in the following screenshot.
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.
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.”