Non-generic ParallelForEach

.NET Framework 4.6.1 ships in its toolbox a set of Control Flow activities, including ParallelForEach<T>, which allows iterating through IEnumerable<T> collections.

ParallelForEach<T> requires its Values property to be of type IEnumerable<T>. This precludes users from iterating over data structures that implement IEnumerable<T> interface (for example, ArrayList). The non-generic version of ParallelForEach<T> overcomes this requirement, at the expense of more run-time complexity for ensuring the compatibility of the types of the values in the collection.

The NonGenericParallelForEach sample shows how to implement a non-generic ParallelForEach<T> activity and its designer. This activity can be used to iterate through ArrayList.

ParallelForEach activity

The C#/Visual Basic foreach statement enumerates the elements of a collection, executing an embedded statement for each element of the collection. The WF equivalent activities are ForEach<T> and ParallelForEach<T>. The ForEach<T> activity contains a list of values and a body. At run time, the list is iterated and the body is executed for each value in the list.

ParallelForEach<T> has a CompletionCondition, so that the ParallelForEach<T> activity could complete early if the evaluation of the CompletionCondition returns true. The CompletionCondition is evaluated after each iteration is completed.

For most cases, the generic version of the activity should be the preferred solution, because it covers most of the scenarios in which it is used and provides type checking at compile time. The non-generic version can be used for iterating through types that implement the non-generic IEnumerable interface.

Class definition

The following code example shows the definition of a non-generic ParallelForEach activity is.

[ContentProperty("Body")]
public class ParallelForEach : NativeActivity
{
    [RequiredArgument]
    [DefaultValue(null)]
    InArgument<IEnumerable> Values { get; set; }

    [DefaultValue(null)]
    [DependsOn("Values")]
    public Activity<bool> CompletionCondition
    [DefaultValue(null)]
    [DependsOn("CompletionCondition")]
    ActivityAction<object> Body { get; set; }
}

Body (optional)
The ActivityAction of type Object, which is executed for each element in the collection. Each individual element is passed into the Body through its Argument property.

Values (optional)
The collection of elements that are iterated over. Ensuring that all elements of the collection are of compatible types is done at run-time.

CompletionCondition (optional)
The CompletionCondition property is evaluated after any iteration completes. If it evaluates to true, then the scheduled pending iterations are canceled. If this property is not set, all activities in the Branches collection execute until completion.

Example of using ParallelForEach

The following code demonstrates how to use the ParallelForEach activity in an application.

string[] names = { "bill", "steve", "ray" };

DelegateInArgument<object> iterationVariable = new DelegateInArgument<object>() { Name = "iterationVariable" };

Activity sampleUsage =
    new ParallelForEach
    {
       Values = new InArgument<IEnumerable>(c=> names),
       Body = new ActivityAction<object>
       {
           Argument = iterationVariable,
           Handler = new WriteLine
           {
               Text = new InArgument<string>(env => string.Format("Hello {0}",                                                               iterationVariable.Get(env)))
           }
       }
   };

ParallelForEach designer

The activity designer for the sample is similar in appearance to the designer provided for the built-in ParallelForEach<T> activity. The designer appears in the toolbox in the Samples, Non-Generic Activities category. The designer is named ParallelForEachWithBodyFactory in the toolbox, because the activity exposes an IActivityTemplateFactory in the toolbox that creates the activity with a properly configured ActivityAction.

public sealed class ParallelForEachWithBodyFactory : IActivityTemplateFactory
{
    public Activity Create(DependencyObject target)
    {
        return new Microsoft.Samples.Activities.Statements.ParallelForEach()
        {
            Body = new ActivityAction<object>()
            {
                Argument = new DelegateInArgument<object>()
                {
                    Name = "item"
                }
            }
        };
    }
}

To run the sample

  1. Set the project of your choice as the startup project of the solution.

    1. CodeTestClient shows how to use the activity using code.

    2. DesignerTestClient shows how to use the activity within the designer.

  2. Build and run the project.