Activity Tree Inspection

Activity tree inspection is used by workflow application authors to inspect the workflows hosted by the application. By using WorkflowInspectionServices, workflows can be searched for specific child activities, individual activities and their properties can be enumerated, and runtime metadata of the activities can be cached at a specific time. This topic provides an overview of WorkflowInspectionServices and how to use it to inspect an activity tree.

Using WorkflowInspectionServices

The GetActivities method is used to enumerate all of the activities in the specified activity tree. GetActivities returns an enumerable that touches all activities within the tree including children, delegate handlers, variable defaults, and argument expressions. In the following example, a workflow definition is created by using a Sequence, While, ForEach<T>, WriteLine, and expressions. After the workflow definition is created, it is invoked and then the InspectActivity method is called.

Variable<List<string>> items = new Variable<List<string>>
{
    Default = new VisualBasicValue<List<string>>("New List(Of String)()")
};

DelegateInArgument<string> item = new DelegateInArgument<string>();

Activity wf = new Sequence
{
    Variables = { items },
    Activities =
    {
        new While((env) => items.Get(env).Count < 5)
        {
            Body = new AddToCollection<string>
            {
                Collection = new InArgument<ICollection<string>>(items),
                Item = new InArgument<string>((env) => "List Item " + (items.Get(env).Count + 1))
            }
        },
        new ForEach<string>
        {
            Values = new InArgument<IEnumerable<string>>(items),
            Body = new ActivityAction<string>
            {
                Argument = item,
                Handler = new WriteLine
                {
                    Text = item
                }
            }
        },
        new Sequence
        {
            Activities =
            {
                new WriteLine
                {
                    Text = "Items added to collection."
                }
            }
        }
    }
};

WorkflowInvoker.Invoke(wf);

InspectActivity(wf, 0);

To enumerate the activities, the GetActivities is called on the root activity, and again recursively on each returned activity. In the following example, the DisplayName of each activity and expression in the activity tree is written to the console.

static void InspectActivity(Activity root, int indent)
{
    // Inspect the activity tree using WorkflowInspectionServices.
    IEnumerator<Activity> activities =
        WorkflowInspectionServices.GetActivities(root).GetEnumerator();

    Console.WriteLine("{0}{1}", new string(' ', indent), root.DisplayName);

    while (activities.MoveNext())
    {
        InspectActivity(activities.Current, indent + 2);
    }
}

This sample code provides the following output.

List Item 1
List Item 2 List Item 3 List Item 4 List Item 5 Items added to collection. Sequence Literal<List<String>>
While
AddToCollection<String>
VariableValue<ICollection<String>>
LambdaValue<String>
LocationReferenceValue<List<String>>
LambdaValue<Boolean>
LocationReferenceValue<List<String>>
ForEach<String>
VariableValue<IEnumerable<String>>
WriteLine
DelegateArgumentValue<String>
Sequence
WriteLine
Literal<String> To retrieve a specific activity instead of enumerating all of the activities, Resolve is used. Both Resolve and GetActivities perform metadata caching if WorkflowInspectionServices.CacheMetadata has not been previously called. If CacheMetadata has been called then GetActivities is based on the existing metadata. Therefore, if tree changes have been made since the last call to CacheMetadata, GetActivities might give unexpected results. If changes have been made to the workflow after calling GetActivities, metadata can be re-cached by calling the ActivityValidationServices Validate method. Caching metadata is discussed in the next section.

Caching Metadata

Caching the metadata for an activity builds and validates a description of the activity’s arguments, variables, child activities, and activity delegates. Metadata, by default, is cached by the runtime when an activity is prepared for execution. If a workflow host author wants to cache the metadata for an activity or activity tree before this, for example to take all of the cost upfront, then CacheMetadata can be used to cache the metadata at the desired time.