Edit

Share via


Custom Composite using Native Activity

The CustomCompositeNativeActivity sample demonstrates how to write a NativeActivity that schedules other Activity objects to control the flow of a workflow's execution. This sample uses two common control flows, Sequence and While, to demonstrate how to do this.

Sample Details

Starting with MySequence, the first thing to notice is that it derives from NativeActivity. NativeActivity is the Activity object that exposes the full breadth of the workflow runtime through the NativeActivityContext passed to the Execute method.

MySequence exposes a public collection of Activity objects that gets populated by the workflow author. Before the workflow is executed, the workflow runtime calls the CacheMetadata method on each activity in a workflow. During this process, the runtime establishes parent-child relationships for data scoping and lifetime management. The default implementation of the CacheMetadata method uses the TypeDescriptor instance class for the MySequence activity to add any public property of type Activity or IEnumerable<Activity> as children of the MySequence activity.

Whenever an activity exposes a public collection of child activities, it is likely those child activities share state. It is a best practice for the parent activity, in this case MySequence, to also expose a collection of variables through which the child activities can accomplish this. Like child activities, the CacheMetadata method adds public properties of type Variable or IEnumerable<Variable> as variables associated with the MySequence activity.

Besides the public variables, which are manipulated by the children of MySequence, MySequence must also keep track of where it is in the execution of its children. It uses a private variable, currentIndex, to accomplish this. This variable is registered as part of the MySequence environment by adding a call to the AddImplementationVariable method within the MySequence activity's CacheMetadata method. The Activity objects added to the MySequence Activities collection cannot access variables added this way.

When MySequence is executed by the runtime, the runtime calls its Execute method, passing in an NativeActivityContext. The NativeActivityContext is the activity's proxy back into the runtime for dereferencing arguments and variables as well as scheduling other Activity objects, or ActivityDelegates. MySequence uses an InternalExecute method to encapsulate the logic of scheduling the first child and all subsequent children in a single method. It starts by dereferencing the currentIndex. If it is equal to the count in the Activities collection, then the sequence is finished, the activity returns without scheduling any work and the runtime moves it into the Closed state. If the currentIndex is less than the count of activities, the next child is obtained from the Activities collection and MySequence calls ScheduleActivity, passing in the child to be scheduled and a CompletionCallback that points at the InternalExecute method. Finally, the currentIndex is incremented and control is yielded back to the runtime. As long as an instance of MySequence has a child Activity object scheduled, the runtime considers it to be in the Executing state.

When the child activity completes, the CompletionCallback is executed. The loop continues from the top. Like Execute, a CompletionCallback takes an NativeActivityContext, giving the implementer access to the runtime.

MyWhile differs from MySequence in that it schedules a single Activity object repeatedly, and in that it uses a Activity<TResult><bool> named Condition to determine whether this scheduling should occur. Like MySequence, MyWhile uses an InternalExecute method to centralize its scheduling logic. It schedules the ConditionActivity<bool> with a CompletionCallback<TResult><bool> named OnEvaluationCompleted. When the execution of Condition is completed, its result becomes available through this CompletionCallback in a strongly typed parameter named result. If true, MyWhile calls ScheduleActivity, passing in the BodyActivity object and InternalExecute as the CompletionCallback. When the execution of Body completes, Condition gets scheduled again in InternalExecute, starting the loop over again. When the Condition returns false, an instance of MyWhile gives control back to the runtime without scheduling the Body and the runtime moves it to the Closed state.

To set up, build, and run the sample

  1. Open the Composite.sln sample solution in Visual Studio.

  2. Build and run the solution.