Working with the ReplicatorActivity and an UntilCondition

The ReplicatorActivity is one of the more interesting Workflow Foundation activities to work with when you have a collection of data and want to iterate over each item. Basically just think of it as a For Each statement in regular code.

To demonstrate the behavior I have created a very small workflow that looks like this in the designer:

image

Or in case you prefer the XOML like this:

<SequentialWorkflowActivity x:Class="WorkflowConsoleApplication5.Workflow1"
                            x:Name="Workflow1"
                            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow">
  <ReplicatorActivity x:Name="replicatorActivity1"
                      ChildInitialized="SetupChildData"
                      Initialized="SetupData"
                      ExecutionType="Sequence">
    <ReplicatorActivity.UntilCondition>
      <CodeCondition Condition="RepeatUntill" />
    </ReplicatorActivity.UntilCondition>
    <CodeActivity x:Name="codeActivity1"
                  ExecuteCode="codeActivity1_ExecuteCode" />
  </ReplicatorActivity>
</SequentialWorkflowActivity>

The codebehind file I used looks like this:

Public Class Workflow1
    Inherits SequentialWorkflowActivity

    Private Sub codeActivity1_ExecuteCode(ByVal sender As Object, _
                                          ByVal e As EventArgs)
        Dim current As CodeActivity = sender
        Dim data = GetCurrentData(current.Parent)
        Console.WriteLine("The data is '{0}'", data)
    End Sub

    Private Sub SetupData(ByVal sender As Object, _
                          ByVal e As EventArgs)
        Dim replicator As ReplicatorActivity = sender

        Dim data() As String = _
        {"One", "Two", "Stop here", "Three"}

        replicator.InitialChildData = data
    End Sub

    Private Sub RepeatUntill(ByVal sender As Object, _
                             ByVal e As ConditionalEventArgs)
        Dim replicator As ReplicatorActivity = sender
        Dim data = GetCurrentData(sender)
        Console.WriteLine("Testing with data '{0}'", data)
        If data = "Stop here" Then
            e.Result = True
        ElseIf replicator.AllChildrenComplete Then
            e.Result = True
        End If
    End Sub

    Private Sub SetupChildData(ByVal sender As Object, _
                               ByVal e As ReplicatorChildEventArgs)
        Console.WriteLine("Starting child with '{0}'", _
                          e.InstanceData)
    End Sub

    Private Function GetCurrentData(ByVal sender As Object)
        Dim replicator As ReplicatorActivity = sender
        Dim result = replicator.CurrentChildData(replicator.CurrentIndex)
        Return result
    End Function
End Class

Using the without an UntilCondition is actually quite simple. The main thing to be aware of the ExecutionType property. In this case I didn’t set it and it defaults to Sequence. This means that each iteration comes after the next iteration and we can use the CurrentIndex and CurrentChildData to retrieve the piece of data we are working with. When we set the ExecutionType to Parallel the ReplicatorActivity behaves very different. In that case each branch is started at the same time so the CurrentIndex, which is scoped to the ReplicatorActivity, doesn’t help you. In fact it will return the last item of the collection every time you try.


In the case of parallel execution the way to get at the data is the ChildInitialized event. This event has a parameter of type ReplicatorChildEventArgs which in turn contains a property InstanceData which is the data this iteration is going to work with. its your responsibility to pass this InstanceData on to the activity inside the ReplicatorActivity. The usual thing to do here is create a custom, possibly sequence, activity with an extra property to hold the data and store it there in the ChildInitialized event.


 


There are a few interesting, and unexpected, thing to be a aware of when you use an UntilCondition with a ReplicatorActivity. When I run this simple test workflow I receive the following output:


image


There are a couple of interesting things to note:


  1. The first time the UntilCondition is called is before the first ChildInitialized event and when we retrieve the current data it actually returns the last item. As far as I can tell there is no standard way to see this is case so if could be a problem you need to take care of it yourself. As far as I am concerned this is a serious shortcoming of the ReplicatorActivity
  2. The UntilCondition fires after and not before each loop is executed. Basically this means it is easy to use the execution result of an iteration and decide to stop further iterations but hard to prevent all remaining iteration, including the current, based on the input data. Again you would need to add some conditional logic inside the ReplicatorActivity. No problem and actually quite a nice behavior but something to be aware of.
  3. The ReplicatorActivity never stops if you don’t tell it to. This is the behavior I most dislike with the ReplicatorActivity. Instead of just stopping and continuing with the next activity when all data is processed it just stops [:(]. Now this isn’t the case when you don’t have an UntilCondition, in that case every item is processed and when done it proceeds with the next activity just as you would expect. No with an UntilCondition you are responsible for stopping and to do so the UntilCondition is actually called one more time after the last item has been processed. The thing to check for is the AllChildrenComplete property of the ReplicatorActivity. When this is true all the data has been processed and you are responsible for stopping the ReplicatorActivity. Now this might not be hard but it always is a major point of confusion and, I presume, catches everyone at least the first time. And worse I can see no benefit at all for this behavior as the ReplicatorActivity just stops and the next activity is never scheduled. As far as I am concerned this is a big “bug” in the behavior of the ReplicatorActivity.

Removing the test for the “Stop here” in the UntilCondition result in the following output:


image


As you can see there is one additional UntilCondition at the end at which time the AllChildrenComplete property is set to true.


 


Enjoy!

www.TheProblemSolver.nl
http://wiki.WindowsWorkflowFoundation.eu

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>