Hello World Workflow Service 4.0
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.
- 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("https://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(), "https://localhost:8080"); IService1 proxy = cf.CreateChannel(); proxy.GetData(); ((IChannel)proxy).Close(); |