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
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)
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.
And for the response you need to name the return value (MethodName)Result.
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.
Comments
Anonymous
August 09, 2010
It looks like Parameters offers String, Int32 and Boolean types (among others) but no Date type (even if you browse), although Message Data allows types that contain Dates. Why is this?Anonymous
August 10, 2010
@TodN - Just use a System.DateTime. You can use the browser to find it.