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 Condition
Activity<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 Body
Activity 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
Open the Composite.sln sample solution in Visual Studio.
Build and run the solution.