Exploring Workflow Foundation Part 4: Custom Composite Activities

October 5, 2008

In the previous posts I introduced custom activities and how they are executed inside a workflow. The samples given where simple activities with no child activities.

In this post I will investigate custom composite activities and what your options are on executing their child activities.

Execution Semantics

The out of the box implementation of Windows Workflow Foundation supports two types of execution semantics: the Sequential workflow and the Statemachine workflow.

Again, I will not give a detailed overview of what the difference is between the two, becaused a lot of people have done an excellent job before me:

There are of course other modes of execution possible, which have other execution semantics. Here again, some people have done a great job on showing how to do this:

Once again: ActivityExecutionContext

If you studdy the above examples you will see that the ActivityExecutionContext is a very important class during the execution of workflows. In my first post, I allready mentioned the articles ActivityExecutionContext in Workflows and Advanced Workflow: Enabling Tricky Scenarios which explains more about the meaning of this class.

The main reason in composite activities for executing a child activity in its own execution context is when you want to execute the child activity more then once. As you know an activity goes through several states and eventually ends in the closed state out of which it can not be revived. If you want to execute your child activity more then once, you shouls somehow be able to create a clone of it and executte this clone. This is exactly what happens when you create a new execution context for an activity.

The general pattern for executing an activity in a new execution context is as follows:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
  // Your stuff ...
  
  newContext = executionContext.ExecutionContextManager.CreateExecutionContext(customActivityOne);
  newContext.ExecuteActivity(newContext.Activity);
  
  // More of your stuff ...
  
}

When creating the new execution context a clone of the provided activity is made, after which you can execute this clone in the newly created context. The original activity remains in it’s original state.

This all looks pretty simple, however, when starting to experiment myself with execution contexts and custom composite activities there where some issues in using execution contexts into which I ran and crippeled my code. Following I will provide some cave-at’s to look out for.

Always close the door behind you

I admit I’ve been cheating: the above code pattern is actually not really complete. If you create a new execution context for an activity, then you must eventually also close this execution context when the activity is executed, that is, the activity enters the closed state. You can do this by registering your composite activity, which acts as the parent of the executed child activity, to be notified of state changes of the child:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
  // Your stuff ...

  // Get notified of your child activity entering the closed state ...
  customActivityOne.RegisterForStatusChange(Activity.ClosedEvent, this);
  
  newContext = executionContext.ExecutionContextManager.CreateExecutionContext(customActivityOne);
  newContext.ExecuteActivity(newContext.Activity);
  
  // More of your stuff ...
  
  return ActivityExecutionStatus.Executing;
  
}

Then you also define a method which will get called when the child enters the closed state. Inside this method you can then close the created execution context:

void 
IActivityEventListener<ActivityExecutionStatusChangedEventArgs>.OnEvent(
  object sender, 
  ActivityExecutionStatusChangedEventArgs e)
{
  ActivityExecutionContext context = sender as ActivityExecutionContext;
 
  // remove notification because the child is no in the closed state
  e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, this);
 
  // Close the door behind you: complete the execution context
  context.ExecutionContextManager.CompleteExecutionContext(
    context.ExecutionContextManager.GetExecutionContext(e.Activity));
 
  context.CloseActivity();
}

Never slam the door into someones face

Allthough the above code is complete, there is however still a cave-at which requires your attention: a composite activity must never move to the closed state when its child activities must still be executed or are still executing.

You can see this by the return code of our composite activity’s Execute method:

  return ActivityExecutionStatus.Executing;

Close ALL doors

But if you can not return ActivityExecutionStatus.Executing from the Execute method, then how can you make you composite activity move to the closed state. The answer is in the method called when the child activity moves to the closed state. At the end of this method there is the following line:

  context.CloseActivity();

The context given to the registered method is NOT the execution context of the child activity, but is the context into which the composite activity is executed. So by calling the CloseActivity method of this parameter, we actually close our composite activity.

Cloning a closed activity: it maybe a copy, but it’s parents are not your parents…

As mentioned above creating a new execution context for an activity actually creates a clone of the activity. If the cloned activity was not closed then the clone is a complete copy of the cloned activity. This means that if your composite activity registered for notification it will also be notified of changes in the cloned activity.

However, if your cloned activity was in the closed state, cloning the activity resets several dependency properties and because registering for notification happens via a dependency property, you lose the notification. This scenario happens when you want to execute the child activity more then once and you clone the allready executed child. This starts a series of events which finaly result in your workflow ending in the ideled state:

  1. You clone the allready executed child and lose the notification registration
  2. If the child eventually moves to the closed state, your composite activity is not notified
  3. Your workflow runs out of things to do
  4. Allthough your workflow ruuns out of things to do, it doesn’t close because the composite activitiy’s execution context ClosActivity() method is never called. Remember this got called during notification ot the child activity moving to the closed state. So, no notification, no calling of this method and no closing of the composite activity
  5. The workflow runtime sees that your workflow hasn’t got anything to do, but not all its activities have closed. The result is that your workflow is idled and can never finish.

The solution is to reregister for notification but on the clone itself and not on the cloned activity.

Thus, you get following code:

  newContext = executionContextManager.CreateExecutionContext(activityToClone);
 
  // Because creating a new ActivityExecutionContext from a closed activity resets
  //  all known dependency properties, we must reregister for the ClosedEvent
  //  (see also the activity CustomCompositeActivityWrongAttempt)
  //  This reregistering must also happen on th eclone, so we use the 
  //  newContext.Activity property which is the activity that will be executed
  //  in the new context and thus the clone
  newContext.Activity.RegisterForStatusChange(Activity.ClosedEvent, this);
   newContext.ExecuteActivity(newContext.Activity);

Downloads

Following is some sample code demonstarting the various scenario’s described above, including some eronious code to demonstrate what happens if you do things wrong:

The code

Links

ActivityExecutionContext
ActivityExecutionContext in Workflows
Advanced Workflow: Enabling Tricky Scenarios

Custom composite activities
Windows Workflow Foundation: Creating a Custom Composite Activity
Creating workflows with custom execution semantics
Custom Activity Designer

Various
Handling Faults in Windows Workflow Custom Activities

About these ads

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: