Sometimes you just don’t want to do an Add Service reference in the client application but still be able to to call a WF4 workflow service. The good thing is that a WF4 workflow service is just another WCF service from the client perspective so almost everything you can do with a regular WCF service you can also do with a workflow service. And calling the service without doing an Add Service Reference first is one of those things.
In these examples I am going to use the default workflow service template just to make it easy to get started. Adjust the client code depending on your service contract.
The first option is to hand craft the service contract on the client and use the ChannelFactory<T>
This option is very simple and straightforward. Basically you are handcrafting a ServiceContract and request/response MessageContract’s to fit the service.
The ServiceContract is nice and simple and looks like this:
[ServiceContract(Name = "IService")]
interface IMyService
{
[OperationContract]
GetDataResponse GetData(GetDataRequest request);
}
The request and response messages are defined using a MessageContract like this:
[MessageContract(IsWrapped = false)]
class GetDataRequest
{
[MessageBodyMember(Name = "int",
Namespace = "http://schemas.microsoft.com/2003/10/Serialization/")]
public int Value { get; set; }
}
[MessageContract(IsWrapped = false)]
class GetDataResponse
{
[MessageBodyMember(Name = "string",
Namespace = "http://schemas.microsoft.com/2003/10/Serialization/")]
public string Value { get; set; }
}
With these pieces in place calling the workflow service is simple using the following code:
static void Main(string[] args)
{
var factory = new ChannelFactory<IMyService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:9199/Service1.xamlx"));
var proxy = factory.CreateChannel();
var response = proxy.GetData(new GetDataRequest() { Value = 42 });
Console.WriteLine(response.Value);
Console.ReadLine();
}
Note the only thing that varies here is the endpoint address.
Getting rid if the ServiceContract
If you don’t want to use a fixed ServiceContract you can use the low level WCF Message API. In this case we use a very simple service contract with the action name of “*” and request/response of type Message. This means we can send any request to any request/response service through this contract. The contract is very simple:
[ServiceContract]
interface IGenericService
{
[OperationContract(Action = "*")]
Message GetData(Message request);
}
The complexity now is in constructing the messages to send but even that isn’t to bad. There are may ways to do this, I chose the XElement API here but you are free to do it any way you like.
static void Main(string[] args)
{
XNamespace ns = "http://schemas.microsoft.com/2003/10/Serialization/";
var factory = new ChannelFactory<IGenericService>(new BasicHttpBinding(), new EndpointAddress("http://localhost:9199/Service1.xamlx"));
var proxy = factory.CreateChannel();
var request = Message.CreateMessage(MessageVersion.Soap11, "http://tempuri.org/IService/GetData", new XElement(ns + "int", 42));
var response = proxy.GetData(request);
var xml = (XElement)XElement.ReadFrom(response.GetReaderAtBodyContents());
var message = xml.Value;
Console.WriteLine("Response is: {0}", message);
Console.ReadLine();
}
With this in place you can create a generic client that can send messages to any WCF service, workflow service or plain old WCF, all you need to do is store the endpoint, operation name and message formats somewhere. I have to say I really like this power and have used it in several applications where flexibility and configurability was important.
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu