Combining WCF and WF4

There are several ways to use WCF in combination with Windows Workflow Foundation 4. The two can be combined inside of a XAMLX file as Ron Jacobs describes here.

Another option is using the WF4 Receive and SendReply activities and hosting the workflow yourself using a WorkflowServiceHost. This is quite a useful option but, at least at the moment, not quite straightforward. When I was trying to get things working using a declarative workflow and the designer Visual Studio 2010 would keep in locking up so all workflows in this example will be coded using C#. Not quite the way things are supposed to be done but it does give some useful insight.

 

Creating a workflow to handle requests

The basic activity we need on the service side is the Receive activity. This allows us to wait for incoming requests and process them, either by using a new workflow or an existing one. If we want to send a response we need to use a SendReply activity as well, if not we are creating a one way contract.

Note that there is not going to be a formal contract as we normally have in WCF. Instead we configure the Receive and SendReply and a contract is derived from that. Not quite sure if I like that as a service contract is supposed to be very much locked down.

The code for the service workflow looks like this:

private static WorkflowElement CreateWorkflow()


{


    var result = new Sequence();


    var input = new Variable<string>();


    result.Variables.Add(input);


    XNamespace ns = "http://tempuri.org";


 


    var handle = new Variable<CorrelationHandle>();


    result.Variables.Add(handle);


 


    var receive = new Receive()


    {


        OperationName = "Operation1",


        ServiceContractName = ns + "MyService",


        Value = new OutArgument<string>(input),


        AdditionalCorrelations = {


           {"ChannelBasedCorrelation", new InArgument<CorrelationHandle>(handle)}


        },


        CanCreateInstance = true


    };


 


    result.Activities.Add(receive);


 


    var write = new WriteLine()


    {


        Text = new InArgument<string>(env => string.Format("The workflow was called with '{0}'.", input.Get(env)))


    };


    result.Activities.Add(write);


 


    var reply = new SendReply()


    {


        Request = receive,


        Value = new InArgument<string>("The result")


    };


    result.Activities.Add(reply);


 


    return result;


}

 

For the most part quite straightforward. We receive a message as defined by the Receive activity Value property, in this case just a string. The message is printed and we return another string through the Value property of the SendReply. Using C# code really shows where we need to use arguments and variables, something that is somewhat hidden when using XAML.

One thing I don’t like is having to create and add the CorrelationHandle. It might be needed internally but I don’t really want to be bothered. Setting the Request property on the SendReply to hook the two together should be enough. Lets hope this disappears before WF4 ships.

 

Hosting the service workflow

With the service workflow in place we need to create a WorkflowServiceHost and actually make the workflow available. The WorkflowServiceHost  is configured using a Service definition which in turn contains a WorkflowServiceImplementation containing the actual workflow and a WCF endpoint. The hosting code looks like this:

XNamespace ns = "http://tempuri.org";


 


var service = new Service();


service.Implementation = new WorkflowServiceImplementation()


{


    Name = ns + "MyService",


    Body = CreateWorkflow()


};


 


service.Endpoints.Add(new Endpoint()


{


    Uri = new Uri("Operation1", UriKind.Relative),


    Binding = new BasicHttpBinding(),


    ServiceContractName = ns + "MyService"


});


 


var host = new WorkflowServiceHost(


    service,


    new Uri("http://localhost:8090/Sequence1"));


 


host.Open();


 


Console.WriteLine("Press enter to stop");


Console.ReadLine();


 


host.Close();

 

The main point here is making sure the Name and ServiceContractName, both for the Endpoint and Receive, are configured correctly or the WorkflowServiceHost will not be able to start and you will get a System.InvalidOperationException with a message something like “Cannot add endpoint because ContractDescription with Name=’MyService’ and Namespace=’http://tempuri.org’ can not be found.”.

 

Creating a calling workflow

With the service side in place we now need to create a client workflow to call into our service. In this case the main activity to use is the Send which sends the request to the WCF service and the ReceiveReply which waits for the response to arrive. The whole is very similar to the service workflow and again we need to use the annoying CorrelationHandle. The workflow code looks like this:

private static WorkflowElement CreateWorkflow()


{


    var result = new Sequence();


 


    XNamespace ns = "http://tempuri.org";


 


    var endpoint = new Endpoint()


    {


        Uri = new Uri("http://localhost:8090/Sequence1/Operation1"),


        Binding = new BasicHttpBinding()


    };


 


    var response = new Variable<string>();


    result.Variables.Add(response);


 


    var handle = new Variable<CorrelationHandle>();


    result.Variables.Add(handle);


 


    var request = new Send()


    {


        OperationName = "Operation1",


        Endpoint = endpoint,


        AdditionalCorrelations =


        {


           {"ChannelBasedCorrelation", new InArgument<CorrelationHandle>(handle)}


        },


        ServiceContractName = ns + "MyService",


        Value = new InArgument<string>("Test")


    };


    result.Activities.Add(request);


 


 


    var reply = new ReceiveReply()


    {


        Request = request,


        Value = new OutArgument<string>(response)


    };


    result.Activities.Add(reply);


 


    var write = new WriteLine()


    {


        Text=new InArgument<string>(response)


    };


    result.Activities.Add(write);


    return result;


}

Just like any other WCF service the main point is to make sure that the service contract names, namespaces and data types match or message will not be processed correctly (or at all). Running this workflow has no special hosting requirements so using the WorkflowInvoker will do just fine.

var workflow = CreateWorkflow();


WorkflowInvoker.Invoke(workflow);


 


Console.WriteLine("Client Done.");


Console.ReadLine();



 







Conclusion



Using WCF and WF4 together is not hard. Unfortunately the designer seems to be letting me down but fortunately everything we can do using the designer can also be done using code.



 



Enjoy!



 



[f1]
[f2]

One thought on “Combining WCF and WF4

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>