A question recently came up on an internal list about how to start a workflow to do some work and then have it accept a message via a Receive activity. This led to an interesting discussion that provides some insight into how the WorkflowServiceHost instantiates workflows in conjunction with the ContextChannel.
Creating a Message Activated Workflow
By default, the WorkflowServiceHost will create a workflow when the following two conditions are true:
- The message received is headed for an operation that is associated with a RecieveActivity that has the CanCreateInstance property set to true
- The message contains no context information
It is interesting to note that you don’t even need to use a binding element collection that contains a ContextBindingElement. The ContextBindingElement is responsible for creating the ContextChannel. The job of the ContextChannel is to do two things on the Receive side
- Extract the context information and pass that along up the stack (hand it off into the service model)
- On the creation, and only on the creation, of a new instance, return the context information to the caller in the header of the response.
So, if we want to create workflows based on messages dropped into an MSMQ queue, we can do that by not trying to add the ContextBindingElement into a custom binding on top of the netMsmqBinding, and associating the operation with a Receive activity with the CanCreateInstance equaling true. Note, that any subsequent communication with the workflow will have to occur with a communication channel over which we can pass context.
Creating a Non-Message Activated Workflow
In the case that this post is about, we do not want to activate off an inbound message. The way to do this doesn’t require much additional work. We first need to make sure we don’t have any of our Receive activities marked with CanCreateInstance to true. This means that no message coming in can activate the workflow. Our workflow will then do some work prior to executing the Receive activity and waiting for the next message. Our workflow will look like this (pretty simple)
When we want to start a workflow, we need to reach into the workflow service host and extract the workflow runtime and initiate the workflow:
WorkflowServiceHost myWorkflowServiceHost = new WorkflowServiceHost(typeof(Workflow1), null); // do some work to set up workflow service host myWorkflowServiceHost.Open(); // on some reason to start the workflow WorkflowRuntime wr = myWorkflowServiceHost.Description.Behaviors.Find<WorkflowRuntimeBehavior>().WorkflowRuntime; WorkflowInstance wi = wr.CreateWorkflow(typeof(Workflow1)); wi.Start(); // need to send wi.InstanceId somewhere for others to communicate with it
The last note is important. In order for a client to eventually be able to communicate to the workflow, the workflow instance Id will need to be relayed to that client.