One of the problems that I’ve been working on solving recently centered around correlation in workflows. In simple terms, where a workflow may produce parallel execution paths, correlation allows the runtime to route events to the right workflow execution path.
In every example that I came across on MSDN and online, the sample cases all assumed intra-workflow correlation as opposed to inter-workflow correlation involving “parent-child” workflow instances.
I posted the initial query – and the solution I used – on this subject over at the MSDN forums:
The trick, as it turns out, is that the CorrelationToken must be initialized in the parent workflow. To accomplish this in the sample, I made a frivolous call to an InitializeCorrelation method on my service interface using a CallExternalMethodActivity, which was marked with the CorrelationInitializer attribute. This activity executes right before the InvokeWorkflowActivity.
|
<FONT face="Courier New"><BR><FONT color=#000000>[Serializable]</FONT><BR><FONT color=#0000ff>public class </FONT><FONT color=#000000>WorkflowCommunicationServiceArgs : ExternalDataEventArgs {</FONT><BR> <FONT color=#0000ff>private string </FONT><FONT color=#000000>key;</FONT><BR><BR> <FONT color=#0000ff>public string </FONT><FONT color=#000000>Key {</FONT><BR> <FONT color=#000000>get { </FONT><FONT color=#0000ff>return </FONT><FONT color=#000000>key; }</FONT><BR> <FONT color=#000000>set { key = value; }</FONT><BR> <FONT color=#000000>}</FONT><BR><BR> <FONT color=#0000ff>public </FONT><FONT color=#000000>WorkflowCommunicationServiceArgs(</FONT><FONT color=#808000>Guid </FONT><FONT color=#000000>instanceId, </FONT><FONT color=#0000ff>string </FONT><FONT color=#000000>key) <BR> : </FONT><FONT color=#0000ff>base</FONT><FONT color=#000000>(instanceId) {</FONT><BR> <FONT color=#0000ff>this</FONT><FONT color=#000000>.key = key;</FONT><BR> <FONT color=#000000>}</FONT><BR><FONT color=#000000>}</FONT><BR><BR><FONT color=#000000>[ExternalDataExchange]</FONT><BR><FONT color=#000000>[CorrelationParameter(</FONT><FONT color=#ff00ff>"key"</FONT><FONT color=#000000>)]</FONT><BR><FONT color=#0000ff>public interface </FONT><FONT color=#000000>IWorkflowCommunicationService {</FONT><BR> <FONT color=#000000>[CorrelationAlias(</FONT><FONT color=#ff00ff>"key"</FONT><FONT color=#000000>, </FONT><FONT color=#ff00ff>"e.Key"</FONT><FONT color=#000000>)]</FONT><BR> <FONT color=#0000ff>event </FONT><FONT color=#808000>EventHandler</FONT><FONT color=#000000><WorkflowCommunicationServiceArgs> ChildCompleted;</FONT><BR><BR> <FONT color=#000000>[CorrelationInitializer]</FONT><BR> <FONT color=#0000ff>void </FONT><FONT color=#000000>InitializeCorrelation(</FONT><FONT color=#0000ff>string </FONT><FONT color=#000000>key);</FONT><BR><BR> <FONT color=#000000>[CorrelationInitializer]</FONT><BR> <FONT color=#0000ff>void </FONT><FONT color=#000000>OnChildCompleted(</FONT><FONT color=#0000ff>string </FONT><FONT color=#000000>key);</FONT><BR><FONT color=#000000>}</FONT><BR><BR><FONT color=#0000ff>public class </FONT><FONT color=#000000>WorkflowCommunicationService : IWorkflowCommunicationService {</FONT><BR> <FONT color=#ff0000>#region </FONT><FONT color=#000000>IWorkflowCommunicationService Members</FONT><BR><BR> <FONT color=#0000ff>public event </FONT><FONT color=#808000>EventHandler</FONT><FONT color=#000000><WorkflowCommunicationServiceArgs> ChildCompleted;</FONT><BR><BR> <FONT color=#0000ff>public void </FONT><FONT color=#000000>InitializeCorrelation(</FONT><FONT color=#0000ff>string </FONT><FONT color=#000000>key) {</FONT><BR> <FONT color=#808000>Console</FONT><FONT color=#000000>.Out.WriteLine(</FONT><FONT color=#ff00ff>"Key -> [{0}]"</FONT><FONT color=#000000>, key);</FONT><BR> <FONT color=#000000>}</FONT><BR><BR> <FONT color=#0000ff>public void </FONT><FONT color=#000000>OnChildCompleted(</FONT><FONT color=#0000ff>string </FONT><FONT color=#000000>key) {</FONT><BR> <FONT color=#808000>MessageBox</FONT><FONT color=#000000>.Show(</FONT><FONT color=#0000ff>string</FONT><FONT color=#000000>.Format(</FONT><FONT color=#ff00ff>"Completed child; Key = [{0}]"</FONT><FONT color=#000000>, key));</FONT><BR><BR> <FONT color=#000000>RaiseChildCompletedEvent(key);</FONT><BR> <FONT color=#000000>}</FONT><BR><BR> <FONT color=#0000ff>public void </FONT><FONT color=#000000>RaiseChildCompletedEvent(</FONT><FONT color=#0000ff>string </FONT><FONT color=#000000>key) {</FONT><BR> <FONT color=#0000ff>if </FONT><FONT color=#000000>(ChildCompleted != </FONT><FONT color=#0000ff>null</FONT><FONT color=#000000>) {</FONT><BR> <FONT color=#000000>ChildCompleted(</FONT><FONT color=#0000ff><BR> null</FONT><FONT color=#000000>, <BR> </FONT><FONT color=#0000ff>new </FONT><FONT color=#000000>WorkflowCommunicationServiceArgs(</FONT><FONT color=#0000ff><BR> new </FONT><FONT color=#808000>Guid</FONT><FONT color=#000000>(</FONT><FONT color=#ff00ff>"5D1667BF-61F6-4bf3-81C0-E70CBE15D2EF"</FONT><FONT color=#000000>), <BR> key<BR> )<BR> );</FONT><BR> <FONT color=#000000>}</FONT><BR> <FONT color=#000000>}</FONT><BR><BR> <FONT color=#ff0000>#endregion</FONT><BR><FONT color=#000000>}</FONT></FONT> |
In essence, the idea is to have two correlation initializers: one utilized by the parent/outer workflow and one utilized by the child/inner workflow when signaling back to the parent. It seems kind of counterintuitive to require two initializations…I’m still not sure how this is working under the covers, but it works đŸ™‚
The working example can be downloaded from: http://www.charliedigital.com/junk/CorrelationTest.Working.zip