다음을 통해 공유


Workflows that don't start with a Receive

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)

image 

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.

Comments

  • Anonymous
    January 17, 2008
    The person who asked the question in the internal discussion list has blogged about it: http://blogs.microsoft.co.il/blogs/sasha/archive/2008/01/11/attaching-a-workflow-instance-to-a-workflow-service-host.aspx

  • Anonymous
    February 16, 2008
    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? Thanks. J

  • Anonymous
    March 09, 2008
    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.

  • Anonymous
    March 10, 2008
    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

  • Anonymous
    March 13, 2008
    joseas 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.


jimitndiaye 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.)