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.
By default, the WorkflowServiceHost will create a workflow when the following two conditions are true:
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
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.
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
// on some reason to start the workflow
WorkflowRuntime wr = myWorkflowServiceHost.Description.Behaviors.Find<WorkflowRuntimeBehavior>().WorkflowRuntime;
WorkflowInstance wi = wr.CreateWorkflow(typeof(Workflow1));
// need to send wi.InstanceId somewhere for others to communicate with it
The person who asked the question in the internal discussion list has blogged about it:
Hi, I am new to WF thus this question may appear elementary. You mentioned passing the workflow instanceID back to the client. Isn't this a violation of SOA paradigm - the client shouldn't be made aware of how the service is implemented, whether it's with workflows or not. That said, you still need to associate client requests with a particular instance of the workflow - so we're back to context, how to retrieve it without making the client aware of it. I'm not sure if explicitly passing the instanceID back to the client would accomplish that. Any ideas?
regarding the instantiation of a new wokflow instance, I understand the WF runtime is very extensible but, in my lazy way of seeing this, can't we focus on the intention of the programmer and have some implicit (static) wiring for simplicity?
WorkflowInstance wi = New NonMessageActivatedWorkflowInstance(typeof(workflow1));
the rest seems redundant and too frustating the memorize.
Note: extensibility should surface only when the developers really needs it.
Working with send and receive activities for workflows hosted in WCF were a continual mystery until I attended a workshop in Nashville given by Miguel Castro (WCF) and Mark Dunn (WF). This resulted in a DNR TV webcast they performed just after our workshop. The webcast is at: http//www.dnrtv.com/default.aspx?showNum=103
I like the ideas of using the runtime to create instances. I agree that the syntax to get to the runtime is a little awkward having to reach into the extensions in order to get it out.
We return the instanceId back to the client as a context token to use for future communication. You can view this as an internal implementation detail, or you can view it as simply a context token. The pattern we're following is similar to other common context exchange patterns, namely the http cookie exchange mechanism.
In the fullness of time, it would be nice to be able to message based, implicit correlation (so that some aspects of my message, like OrderID perform the correlation.)