Using the ReplicatorActivity in Parallel mode
I have heard quite a few times that the ReplicatorActivity can only be use in parallel mode with a custom activity. The reason being that you need an extra property to store the current child data.
When the ReplicatorActivity works in sequential mode the following code works just fine:
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
int value = (int)replicatorActivity1.CurrentChildData[replicatorActivity1.CurrentIndex];
Console.WriteLine("Loop value = {0}", value);
}
However when run in parallel the current index always point to the last item so this doesn't work. As an aside I think this is a bug as it results in hard to find errors. IMHO the ReplicatorActivity CurrentIndex should be -1 or some other value that would trigger either an IndexOutOfRangeException or an InvalidOperationException. Unfortunately that is not the case
.
Instead when we set the mode to parallel we need to use the ChildInitialized event to save the child data for that loop. So we need a place to store it.
Traditional thinking results in an extra property being added that is being scoped to the loop and an extra property means a custom activity to add it to.
But wait a minute doesn't every workflow foundation activity derive from DependencyObject and isn't that class all about using dependency properties?
Yes it is and that is exactly the reason you don't need a custom activity. In fact you can add as much extra data to any activity as you like!
How to add custom data to the standard activities
The trick is in using a DependencyProperty that is created using the DependencyProperty.RegisterAttached() function. This creates an attached property you can use to store data in activities that have no clue about the data. So the first step is to create a dependency property like this:
public static DependencyProperty LoopValueProperty =
DependencyProperty.RegisterAttached("LoopValue", typeof(int), typeof(Workflow1));
Now in the ChildInitialized event we van store the loop value in child activity like this:
private void replicatorActivity1_ChildInitialized(
object sender,
ReplicatorChildEventArgs e)
{
e.Activity.SetValue(LoopValueProperty, e.InstanceData);
}
Easy right 
So if I add a code activity as the child just to print the data all I need is the following code:
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Activity activity = (Activity)sender;
int value = (int)activity.GetValue(LoopValueProperty);
Console.WriteLine("The current value = {0}", value);
}
A slightly larger example showing the parallel behavior
Below is a slightly more complex example showing this. I have added a DelayActivity with a random timeout just to show that the ReplicatorActivity is really running in parallel.
The workflow looks like this:
And when run this produces the following output:
The complete code behind file looks like this:
public sealed partial class Workflow1 : SequentialWorkflowActivity
{
public Workflow1()
{
InitializeComponent();
}
public static DependencyProperty LoopValueProperty =
DependencyProperty.RegisterAttached("LoopValue", typeof(int), typeof(Workflow1));
private void replicatorActivity1_Initialized(object sender, EventArgs e)
{
ReplicatorActivity replicator = (ReplicatorActivity)sender;
replicator.InitialChildData = new List<int>(Enumerable.Range(1, 20));
}
private void delayActivity1_InitializeTimeoutDuration(object sender, EventArgs e)
{
DelayActivity delay = (DelayActivity)sender;
Random random = new Random();
delay.TimeoutDuration = TimeSpan.FromSeconds(random.Next(5));
}
private void replicatorActivity1_ChildInitialized(
object sender,
ReplicatorChildEventArgs e)
{
e.Activity.SetValue(LoopValueProperty, e.InstanceData);
}
private void codeActivity1_ExecuteCode(object sender, EventArgs e)
{
Activity activity = (Activity)sender;
int value = (int)activity.Parent.GetValue(LoopValueProperty);
Console.WriteLine("The current value = {0}", value);
}
}
Enjoy!
www.TheProblemSolver.nl
Wiki.WindowsWorkflowFoundation.eu