Exposing data with CacheMetadata

This topic applies to Windows Workflow Foundation 4 (WF4).

Before executing an activity, the workflow runtime obtains all of the information about the activity that it needs in order to maintain its execution. The workflow runtime gets this information during the execution of the CacheMetadata method. The default implementation of this method provides the runtime with all of the public arguments, variables, and child activities exposed by the activity at the time it is executed; if the activity needs to give more information to the runtime than this (such as private members, or activities to be scheduled by the activity), this method can be overridden to provide it.

Default CacheMetadata behavior

The default implementation of CacheMetadata for activities that derive from NativeActivity processes the following method types in the following ways:

  • InArgument, OutArgument, or InOutArgument (generic arguments): These arguments are exposed to the runtime as arguments with a name and type equal to the exposed property name and type, the appropriate argument direction, and some validation data.

  • Variable or any subclass thereof: These members are exposed to the runtime as public variables.

  • Activity or any subclass thereof: These members are exposed to the runtime as public child activities. The default behavior can be implemented explicity by calling AddImportedChild, passing in the child activity.

  • ActivityDelegate or any subclass thereof: These members are exposed to the runtime as public delegates.

  • ICollection of type Variable: All elements in the collection are exposed to the runtime as public variables.

  • ICollection of type Activity: All elements in the collection are exposed to the runtime as public children.

  • ICollection of type ActivityDelegate: All elements in the collection are exposed to the runtime as public delegates.

The CacheMetadata for activities that derive from Activity, CodeActivity, and AsyncCodeActivity also function as above, except for the following differences:

  • Classes that derive from Activity cannot schedule child activities or delegates, so such members are exposed as imported children and delegates.

  • Classes that derive from CodeActivity and AsyncCodeActivity do not support variables, children, or delegates, so only arguments will be exposed.

Overriding CacheMetadata to provide information to the runtime

The following code snippet demonstrates how to add information about members to an activity’s metadata during the execution of the CacheMetadata method. Note that the base of the method is called to cache all public data about the activity.

protected override void CacheMetadata(NativeActivityMetadata metadata)
{    
    base.CacheMetadata(metadata);
    metadata.AddImplementationChild(this._writeLine);
    metadata.AddVariable(this._myVariable);
    metadata.AddImplementationVariable(this._myImplementationVariable);
    
    RuntimeArgument argument = new RuntimeArgument("MyArgument", ArgumentDirection.In, typeof(SomeType));
    metadata.Bind(argument, this.SomeName);
    metadata.AddArgument(argument);
}

Using CacheMetadata to expose implementation children

In order to pass data to child activities that are to be scheduled by an activity using variables, it is necessary to add the variables as implementation variables; public variables cannot have their values set this way. The reason for this is that activities are intended to be executed more as implementations of functions (which have parameters), rather than encapsulated classes (which have properties). However, there are situations in which the arguments must be explicitly set, such as when using ScheduleActivity, since the scheduled activity doesn't have access to the parent activity's arguments in the way a child activity would.

The following code snippet demonstrates how to pass an argument form a native activity into a scheduled activity using CacheMetadata.

public sealed class ChildActivity : NativeActivity
{
    public WriteLine _writeLine;
    public InArgument<string> Message { get; set; }
    private Variable<string> MessageVariable { get; set; }
    public ChildActivity()
    {
        MessageVariable = new Variable<string>();
        _writeLine = new WriteLine
        {
            Text = new InArgument<string>(MessageVariable),
        };
    }
    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        base.CacheMetadata(metadata);
        metadata.AddImplementationVariable(this.MessageVariable);
        metadata.AddImplementationChild(this._writeLine);
    }
    protected override void Execute(NativeActivityContext context)
    {
        string configuredMessage = context.GetValue(Message);
        context.SetValue(MessageVariable, configuredMessage);
        context.ScheduleActivity(this._writeLine);
    }
}

When adding a child activity using AddChild, CacheMetadata must be called first or else the following error will occur.

Unhandled Exception: System.ArgumentException: The provided activity was not part of this workflow definition when its metadata was being processed.  The problematic activity named 'WriteLine' was provided by the activity named 'ChildActivity'.

To resolve, move the call to base.CacheMetadata before the call to AddChild.