Note: Cross posted from Sajay.
Permalink

Building a workflow service using 4.0 gives a very neat set of capabilities from both WF and WCF. Similar to WCF we can fully define a workflow either in code or otherwise just using Xaml. Here I chose a fully code based approach. If you just want the source you can download it here.

The Service Contract

We generally can take a workflow first approach or a contract first. To keep things simple, I have a very bare service contract as shown below.

 

[ServiceContract]
interface IService1
{
    [OperationContract]
   
void GetData();
}

I also hold on to the contract description. This is just convenience and to avoid some constants like service name etc.

ContractDescription description = ContractDescription.GetContract(typeof(IService1));

The Operation

The idea here is to build the service bottom up from operation we need to perform. I can think of my workflow as composition of activities. The functionality of my service is quite simple here. All I do is write a message to the console. So the body of my operation will look something like this.

new WriteLine

   
TextWriter = Console.Out,
   
Text ="Hello Workflow."
},

The Implementation

Now I need to make this an operation. An operation has an associated message exchange pattern, commonly referred to as MEP. In my case it's a simple receive reply pattern. This means I have to compose my work in between a Receive and a SendReply. I am also going to tie this all up in Sequence as shown below.

Sequence sequence = new Sequence
{
   
Activities =
   
{
       
receive,
       
new WriteLine 
       
{
           
TextWriter = Console.Out, Text ="Hello Workflow."
       
},
       
reply
   
}
};

The Message Exchange

The next thing is to lay out my Receive and Reply which will define the wire format of the messages.

  1. The receive needs to know the operation that the client can invoke. Also I need to make sure that the workflow can be instantiated by this particular operation. For this I need to set the CanCreateInstance property. Also the serializer used by default is the DataContractSerializer.

Receive receive = new Receive
{
    OperationName = description.Operations[0].Name, 
    CanCreateInstance = true
};

  • The Reply is send back to the client using my SendReply activity. This reply is a part of a conversation and it needs to know which request it would respond to. I can do this by pointing the SendReply to the instance of the corresponding Receive. Also I don't have any data here so it is an empty parameter list. (the operation is not Messages contract based in my case but this is generally a better option for more control and performance.)

SendReply reply = new SendReply

    Request = receive, 
   
Content = new SendParametersContent { }
};

  • I now need to define the workflow service which would hold this Sequence and also need to specify the full name of the service definition. For this I can use the description object to get the name, namespace etc.

WorkflowService serviceDefinition = new WorkflowService
{
    Body = sequence,
    Name = XName.Get(description.Name, description.Namespace)
};

The Host

The next step is to give this definition to the workflow service host so that it can start the service. Here I self host the workflow as shown below.

WorkflowServiceHost host = new WorkflowServiceHost(serviceDefinition, 
                                                   new Uri("http://localhost:8080"));
host.AddServiceEndpoint(serviceDefinition.Name, new BasicHttpBinding(), "");
host.Open();

The Test

Finally I can test the workflow using a simple proxy.

ChannelFactory<IService1> cf = new ChannelFactory<IService1>(new BasicHttpBinding(),
                                                              "http://localhost:8080"
);
IService1 proxy = cf.CreateChannel();
proxy.GetData();
((IChannel)proxy).Close();