I built a sample (you can download it here)
based upon a question asked in the WF forums last night (you can see the post here).

I wanted to post it here so I could point out some of the interesting features it
has in it.

First – there is a CallWorkflowActivity – which creates and executes another workflow
in a synchronous manner.  The WF built-in InvokeWorkflowActivity invokes another
workflow – but does it asynchronously.    It turns out to do is synchronously
you have to build a custom activity – mostly because synchronously you have to make
some assumptions about the workflow you are calling.   In my example – the
assumption I am making is that the “called” workflow will have parameters to pass
in, and parameters that are return – but there is no other communication between the
host and the workflow.

Now – since my CallWorkflowActivity can’t talk to the WorkflowRuntime directly – in
order to make it work I implemented a service to add to the WorkflowRuntime. 
Why can’t the activity talk to the WorkflowRuntime?  Well that could create some
interesting problems – like what if an Activity could ask the WorkflowRuntime to persist
the WorkflowInstance the Activity is being executed inside of?  The Activity
would be executing waiting for persistence, the WorkflowRuntime would be waiting to
persist until the Activity completed – classic deadlock.

 

InvokeWorkflow uses a service that is installed by default – IStartWorkflow – which
works create in the async case – but doesn’t work in the sync case.  So I just
created a custom service.  One aspect of WF that I try to emphasize to people
when I do my WF trainings is that even though there are well-known services inside
of the WorkflowRuntime – you can add any number of services which won’t be known to
the WorkflowRuntime – but can be used by your Activities.  In fact this is necessary
in some cases (like this one) where the code you want to execute would be “illegal”
inside of Activity.Execute (things like spawning threads or anything that would go
against the model of WF in terms of what Activities should do).  Now in this
case I made the Service derive from WorkflowRuntimeService because my service needs
the WorkflowRuntime to do its job, and that is the easiest way to get a reference
to the WorkflowRuntime.   But it isn’t a requirement that a service added
via WorkflowRuntime.AddService derive from WorkflowRuntimeService. 

So here is how my service executes a workflow:

public class CallWorkflowService
: WorkflowRuntimeService
{

public void StartWorkflow(Type
workflowType,Dictionary<string,object>
inparms,Guid caller,IComparable qn)
{
WorkflowRuntime wr = this.Runtime;
WorkflowInstance wi = wr.CreateWorkflow(workflowType,inparms);
wi.Start();
ManualWorkflowSchedulerService ss = wr.GetService<ManualWorkflowSchedulerService>();
if (ss
!= null)
ss.RunWorkflow(wi.InstanceId);
EventHandler<WorkflowCompletedEventArgs> d = null;
d = delegate(object o,
WorkflowCompletedEventArgs e)
{
if (e.WorkflowInstance.InstanceId
==wi.InstanceId)
{
wr.WorkflowCompleted -= d;
WorkflowInstance c = wr.GetWorkflow(caller);
c.EnqueueItem(qn, e.OutputParameters, null, null);
}
};
EventHandler<WorkflowTerminatedEventArgs> te = null;
te = delegate(object o,
WorkflowTerminatedEventArgs e)
{
if (e.WorkflowInstance.InstanceId
== wi.InstanceId)
{
wr.WorkflowTerminated -= te;
WorkflowInstance c = wr.GetWorkflow(caller);
c.EnqueueItem(qn, new Exception(“Called
Workflow Terminated”
, e.Exception), null, null);
}
};
wr.WorkflowCompleted += d;
wr.WorkflowTerminated += te;

}

}

 

Notice the use of anonymous delegates really helps in this case.  No need to
keep state about workflow instances around in the service – the anonymous delegates
get registered and unregistered for each Workflow execution. 

To get the data back to the Activity – the WorkflowQueue name that the Activity created
– so the Activity can return ActivityExecutionStatus.Executing from Execute – and
it waits for an item to come back on the Queue.

If the Workflow terminates – the service sends an exception into the WorkflowQueue
– which causes that exception to promolgate to the workflow itself.

Another interesting part of this sample is the use of the WorkflowParameterBindingCollection
– similar to the way that the InvokeWorkflow,CallExternalMethod and other built-in
activities do.  Make note of the DependencyProperty declaration and the call
to base.SetReadOnlyProperty in the constructor – both are necessary to get Collections
to serialize correctly into your .designer file.