How to make a WorkflowService implement a contract

You may have noticed that WorkflowServices have two ways of operating.  One way is to pass message content and the other way is to use message parameters.  I have always used message content because it seemed like the easiest thing to do.

Download the Sample Code on MSDN Code Gallery

Get Microsoft Silverlight

Today I wanted to write some test code that would new up a client proxy with a Channel factory without having to go through generating a service reference.  I was just writing some test code so I figured I would create an interface that looked like the workflow service and create the proxy.

Given this workflow service (the default template)

image

I created this interface

 using System.ServiceModel;



namespace Contract

{

    [ServiceContract]

    public interface IService1

    {

        [OperationContract]

        string GetData(int data);

    }

}

It didn’t work.  I got this error

System.ServiceModel.ActionNotSupportedException was unhandled

Message=The message with Action '<a href="https://tempuri.org/IService1/GetData'">https://tempuri.org/IService1/GetData'</a> cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).

What to Do

When I face a problem like this I immediately stop trying to fix it in the big project and create a very simple spike project where I can explore what is going on.  I decided to create a project with a WorkflowService and a WCF Service then build a console app that could call both of them.  This allowed me to compare the WCF Service to the Workflow Service and here is what I found

Workflow Service Request

 <s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">

  <s:Header>

    <Action s:mustUnderstand="1" xmlns="https://schemas.microsoft.com/ws/2005/05/addressing/none">https://tempuri.org/IService1/GetData</Action>

  </s:Header>

  <s:Body>

    <int xmlns="https://schemas.microsoft.com/2003/10/Serialization/">1</int>

  </s:Body>

</s:Envelope>

Workflow Service Response

 <s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">

  <s:Header />

  <s:Body>

    <string xmlns="https://schemas.microsoft.com/2003/10/Serialization/">1</string>

  </s:Body>

</s:Envelope>

WCF Service Request

 <s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">

  <s:Header>

    <Action s:mustUnderstand="1" xmlns="https://schemas.microsoft.com/ws/2005/05/addressing/none">https://tempuri.org/IService1/GetData</Action>

  </s:Header>

  <s:Body>

    <GetData xmlns="https://tempuri.org/">

      <data>1</data>

    </GetData>

  </s:Body>

</s:Envelope>

WCF Service Response

 <s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">

  <s:Header />

  <s:Body>

    <GetDataResponse xmlns="https://tempuri.org/">

      <GetDataResult>1</GetDataResult>

    </GetDataResponse>

  </s:Body>

</s:Envelope>

As you can see the WCF service wraps up the parameters inside a request/response message where the Workflow Service (using message content) simply serializes the data into the message body.

The WCF Client proxy (created by the channel factory based on the interface) does not understand the message content as sent by the Workflow service.  Of course you can always add a service reference and generate the correct client proxy.

The Solution

The fix for this is to change the way that the messaging activities deal with the content.  If you want to create a Workflow Service where the content looks like it would for a WCF service then you need to use Message Parameters. 

SNAGHTMLa26cd9

And for the response you need to name the return value (MethodName)Result.

SNAGHTMLa32af9

Now when I run it everything is working as it should and the Workflow Service request response content looks exactly like the WCF Service content.

Troubleshooting

If you are still having trouble, don't forget to set the ServiceContractName (and namespace if necessary) on each of the receive activities.