Workflow Receive activity and message correlation

With WF4 it is quite easy to use data that is part of the request message, for example an order identifier, to route multiple WCF messages to the same workflow. It is also possible to have multiple messages that can start a new workflow. In this blog post I am going to show both these concepts in a single workflow. This workflow is going to be able to receive three different messages, AddItem, AddExpensiveItem and Submit.  Either AddItem or AddExpensiveItem can start a new workflow while Submit can only be done with an existing workflow.

The final workflow looks like this±

image

 

To create the workflow you need to start by creating a new WCF Workflow Service Application in Visual Studio 2010. Next delete the default activities that have been created by the template and replace it with a Sequence activity. Add a variable named TotalAmount of type Int32 and a Boolean variable named OrderSubmitted. Add a DoWhile activity and set its condition to OrderSubmitted = false so it will keep on executing until we set the OrderSubmitted variable to true.

So much for the boring part, now we get to the real message part.

First we need to add a Pick activity. This is needed because we want to receive multiple different messages without specifying an order. Add a third PickBrach so we get a total of three branches and a a Receive activity to each Trigger. Set all three ServiceContractName’s to OrderService and set the OperationName to AddItem, AddExpensiveItem and Submit respectively. Now the AddItem and AddExpensiveItem requests are allowed to start a new workflow so check the CanCreateInstance for both of them.

Next the important part for the correlation bit. All three requests need to share some common piece of data.Below is the Content definition of the AddItem request.

image

Both the AddExpensiveItem and Submit requests only contain the orderId parameter

image

The important is that they all share the same orderId that the client needs to provide.

 

That takes care of sending some common data but we still need to configure the workflow to use that to correlate requests to the same workflow. This last part is done using the CorrelatesOn property of the Receive activity. In all three point this to the orderId being passed in and make sure all three have a matching key.

image

 

After we have added the processing part to the Action part of each PickBranch we are all set to go.

Note: to create the SendReply, just right click the Receive activity and select Create SendReply. This will be placed on the clipboard so now you can paste it into the Action Sequence.

 

Running the workflow

Press F5 to activate the WCF Test Client. You should see three possible actions, either AddItem or AddExpensiveItem can start a workflow or send messages to an existing workflow. It just depends on whether the orderId passed in can be correlated to an existing workflow. If so that workflow receives the message, otherwise a new workflow is started.

image

The Submit message on the other hand can only be executed with an existing workflow.

 

 

Just in case anyone is interested below is the complete XAML for the workflow:

<WorkflowService mc:Ignorable="sap" ConfigurationName="Service1" sap:VirtualizedContainerService.HintSize="1193,850" Name="Service1" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces" xmlns="http://schemas.microsoft.com/netfx/2009/xaml/servicemodel" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mv="clr-namespace:Microsoft.VisualBasic;assembly=System" xmlns:mva="clr-namespace:Microsoft.VisualBasic.Activities;assembly=System.Activities" xmlns:p="http://schemas.microsoft.com/netfx/2009/xaml/activities" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:s1="clr-namespace:System;assembly=System" xmlns:s2="clr-namespace:System;assembly=System.Xml" xmlns:s3="clr-namespace:System;assembly=System.Core" xmlns:s4="clr-namespace:System;assembly=System.ServiceModel" xmlns:sa="clr-namespace:System.Activities;assembly=System.Activities" xmlns:sad="clr-namespace:System.Activities.Debugger;assembly=System.Activities" xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation" xmlns:scg="clr-namespace:System.Collections.Generic;assembly=System" xmlns:scg1="clr-namespace:System.Collections.Generic;assembly=System.ServiceModel" xmlns:scg2="clr-namespace:System.Collections.Generic;assembly=System.Core" xmlns:scg3="clr-namespace:System.Collections.Generic;assembly=mscorlib" xmlns:sd="clr-namespace:System.Data;assembly=System.Data" xmlns:sl="clr-namespace:System.Linq;assembly=System.Core" xmlns:ssa="clr-namespace:System.ServiceModel.Activities;assembly=System.ServiceModel.Activities" xmlns:ssx="clr-namespace:System.ServiceModel.XamlIntegration;assembly=System.ServiceModel" xmlns:st="clr-namespace:System.Text;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">


  <p:Sequence sad:XamlDebuggerXmlReader.FileName="c:\users\maurice\documents\visual studio 2010\Projects\DeclarativeServiceLibrary1\DeclarativeServiceLibrary1\Service1.xamlx" sap:VirtualizedContainerService.HintSize="1163,820" mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces">


    <p:Sequence.Variables>


      <p:Variable x:TypeArguments="x:Int32" Name="TotalAmount" />


    </p:Sequence.Variables>


    <sap:WorkflowViewStateService.ViewState>


      <scg3:Dictionary x:TypeArguments="x:String, x:Object">


        <x:Boolean x:Key="IsExpanded">True</x:Boolean>


      </scg3:Dictionary>


    </sap:WorkflowViewStateService.ViewState>


    <p:DoWhile sap:VirtualizedContainerService.HintSize="1141,696">


      <p:DoWhile.Variables>


        <p:Variable x:TypeArguments="x:Boolean" Name="OrderSubmitted" />


      </p:DoWhile.Variables>


      <p:DoWhile.Condition>[OrderSubmitted = False]</p:DoWhile.Condition>


      <p:Pick sap:VirtualizedContainerService.HintSize="1115,576">


        <p:PickBranch DisplayName="Branch1" sap:VirtualizedContainerService.HintSize="307,530">


          <p:PickBranch.Variables>


            <p:Variable x:TypeArguments="CorrelationHandle" Name="__handle1" />


            <p:Variable x:TypeArguments="x:Int32" Name="Price" />


          </p:PickBranch.Variables>


          <p:PickBranch.Trigger>


            <x:Reference>__ReferenceID0</x:Reference>


          </p:PickBranch.Trigger>


          <p:Sequence sap:VirtualizedContainerService.HintSize="277,312">


            <sap:WorkflowViewStateService.ViewState>


              <scg3:Dictionary x:TypeArguments="x:String, x:Object">


                <x:Boolean x:Key="IsExpanded">True</x:Boolean>


              </scg3:Dictionary>


            </sap:WorkflowViewStateService.ViewState>


            <p:Assign sap:VirtualizedContainerService.HintSize="255,58">


              <p:Assign.To>


                <p:OutArgument x:TypeArguments="x:Int32">[TotalAmount]</p:OutArgument>


              </p:Assign.To>


              <p:Assign.Value>


                <p:InArgument x:TypeArguments="x:Int32">[TotalAmount + Price]</p:InArgument>


              </p:Assign.Value>


            </p:Assign>


            <SendReply DisplayName="SendReplyToReceive" sap:VirtualizedContainerService.HintSize="255,90">


              <SendReply.Request>


                <Receive x:Name="__ReferenceID0" CanCreateInstance="True" sap:VirtualizedContainerService.HintSize="277,100" OperationName="AddItem" ServiceContractName="OrderService">


                  <Receive.CorrelatesOn>


                    <XPathMessageQuery x:Key="key1">


                      <XPathMessageQuery.Namespaces>


                        <ssx:XPathMessageContextMarkup>


                          <x:String x:Key="xgSc">http://tempuri.org/</x:String>


                        </ssx:XPathMessageContextMarkup>


                      </XPathMessageQuery.Namespaces>sm:body()/xgSc:AddItem/xgSc:orderId</XPathMessageQuery>


                  </Receive.CorrelatesOn>


                  <Receive.CorrelationInitializers>


                    <RequestReplyCorrelationInitializer CorrelationHandle="[__handle1]" />


                  </Receive.CorrelationInitializers>


                  <ReceiveParametersContent>


                    <p:OutArgument x:TypeArguments="x:Int32" x:Key="orderId" />


                    <p:OutArgument x:TypeArguments="x:String" x:Key="item" />


                    <p:OutArgument x:TypeArguments="x:Int32" x:Key="price">[Price]</p:OutArgument>


                  </ReceiveParametersContent>


                </Receive>


              </SendReply.Request>


              <SendParametersContent>


                <p:InArgument x:TypeArguments="x:Int32" x:Key="Total">[TotalAmount]</p:InArgument>


              </SendParametersContent>


            </SendReply>


          </p:Sequence>


        </p:PickBranch>


        <p:PickBranch sap:VirtualizedContainerService.HintSize="307,530">


          <p:PickBranch.Variables>


            <p:Variable x:TypeArguments="CorrelationHandle" Name="__handle1" />


          </p:PickBranch.Variables>


          <p:PickBranch.Trigger>


            <x:Reference>__ReferenceID1</x:Reference>


          </p:PickBranch.Trigger>


          <p:Sequence sap:VirtualizedContainerService.HintSize="277,312">


            <sap:WorkflowViewStateService.ViewState>


              <scg3:Dictionary x:TypeArguments="x:String, x:Object">


                <x:Boolean x:Key="IsExpanded">True</x:Boolean>


              </scg3:Dictionary>


            </sap:WorkflowViewStateService.ViewState>


            <p:Assign sap:VirtualizedContainerService.HintSize="255,58">


              <p:Assign.To>


                <p:OutArgument x:TypeArguments="x:Int32">[TotalAmount]</p:OutArgument>


              </p:Assign.To>


              <p:Assign.Value>


                <p:InArgument x:TypeArguments="x:Int32">[TotalAmount + 100]</p:InArgument>


              </p:Assign.Value>


            </p:Assign>


            <SendReply DisplayName="SendReplyToReceive" sap:VirtualizedContainerService.HintSize="255,90">


              <SendReply.Request>


                <Receive x:Name="__ReferenceID1" CanCreateInstance="True" sap:VirtualizedContainerService.HintSize="277,100" OperationName="AddExpensiveItem" ServiceContractName="OrderService">


                  <Receive.CorrelatesOn>


                    <XPathMessageQuery x:Key="key1">


                      <XPathMessageQuery.Namespaces>


                        <ssx:XPathMessageContextMarkup>


                          <x:String x:Key="xgSc">http://tempuri.org/</x:String>


                        </ssx:XPathMessageContextMarkup>


                      </XPathMessageQuery.Namespaces>sm:body()/xgSc:AddExpensiveItem/xgSc:orderId</XPathMessageQuery>


                  </Receive.CorrelatesOn>


                  <Receive.CorrelationInitializers>


                    <RequestReplyCorrelationInitializer CorrelationHandle="[__handle1]" />


                  </Receive.CorrelationInitializers>


                  <ReceiveParametersContent>


                    <p:OutArgument x:TypeArguments="x:Int32" x:Key="orderId" />


                  </ReceiveParametersContent>


                </Receive>


              </SendReply.Request>


              <SendParametersContent>


                <p:InArgument x:TypeArguments="x:Int32" x:Key="Total">[TotalAmount]</p:InArgument>


              </SendParametersContent>


            </SendReply>


          </p:Sequence>


        </p:PickBranch>


        <p:PickBranch DisplayName="Branch2" sap:VirtualizedContainerService.HintSize="307,530">


          <p:PickBranch.Variables>


            <p:Variable x:TypeArguments="CorrelationHandle" Name="__handle2" />


          </p:PickBranch.Variables>


          <p:PickBranch.Trigger>


            <x:Reference>__ReferenceID2</x:Reference>


          </p:PickBranch.Trigger>


          <p:Sequence sap:VirtualizedContainerService.HintSize="277,312">


            <sap:WorkflowViewStateService.ViewState>


              <scg3:Dictionary x:TypeArguments="x:String, x:Object">


                <x:Boolean x:Key="IsExpanded">True</x:Boolean>


              </scg3:Dictionary>


            </sap:WorkflowViewStateService.ViewState>


            <SendReply DisplayName="SendReplyToReceive" sap:VirtualizedContainerService.HintSize="255,90">


              <SendReply.Request>


                <Receive x:Name="__ReferenceID2" sap:VirtualizedContainerService.HintSize="277,100" OperationName="Submit" ServiceContractName="OrderService">


                  <Receive.CorrelatesOn>


                    <XPathMessageQuery x:Key="key1">


                      <XPathMessageQuery.Namespaces>


                        <ssx:XPathMessageContextMarkup>


                          <x:String x:Key="xgSc">http://tempuri.org/</x:String>


                        </ssx:XPathMessageContextMarkup>


                      </XPathMessageQuery.Namespaces>sm:body()/xgSc:Submit/xgSc:orderId</XPathMessageQuery>


                  </Receive.CorrelatesOn>


                  <Receive.CorrelationInitializers>


                    <RequestReplyCorrelationInitializer CorrelationHandle="[__handle2]" />


                  </Receive.CorrelationInitializers>


                  <ReceiveParametersContent>


                    <p:OutArgument x:TypeArguments="x:Int32" x:Key="orderId" />


                  </ReceiveParametersContent>


                </Receive>


              </SendReply.Request>


            </SendReply>


            <p:Assign sap:VirtualizedContainerService.HintSize="255,58">


              <p:Assign.To>


                <p:OutArgument x:TypeArguments="x:Boolean">[OrderSubmitted]</p:OutArgument>


              </p:Assign.To>


              <p:Assign.Value>


                <p:InArgument x:TypeArguments="x:Boolean">True</p:InArgument>


              </p:Assign.Value>


            </p:Assign>


          </p:Sequence>


        </p:PickBranch>


      </p:Pick>


    </p:DoWhile>


  </p:Sequence>


</WorkflowService>



 



Enjoy!



 



[f1]
[f2]

6 thoughts on “Workflow Receive activity and message correlation

  1. First of all: Thanks a lot for this example. Helped me a lot!

    I have a question: Let’s assume the following scenario: You have a workflow service containing multiple receive activities that can accept correlated messages in the sense described above. This receive activities might be scattered over a flowchart-like workflow.
    What happens now if you call a service operation mapped to one of these receive activities that is already dispatched or is still to come from the flowchart’s point of view for the addressed workflow instance?
    Is that call just ignored and you get a timeout or what kind of fault message is to be expected on the proxy side?

    I have to admit that I am quite new to WF. I just have no idea how to enforce the ‘state’ of the workflow in a workflow service.

    Thank you very much for any response
    Best regards

  2. Ok I will have a look at that. Thanks once again and good luck on Sunday (in any case you are interested in WC2010 and going for Oranje :-).

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>