Basic Activity Scheduling in Workflow 4.0

…NativeActivity. Provides all of the features of CodeActivity, plus aborting activity execution, canceling child activity execution, using bookmarks, and scheduling activities…

As of .Net Fx 4.0 Beta 2, the Activity class hierarchy looks a bit like this:image

Actually this is only a tiny chunk of the Activity class Hierarchy, but it does include every class you might seriously consider trying to derive from in code. (Apologies if the layout is bad here.)

 

Now which of these activities is most interesting? You might seriously consider deriving from CodeActivity or AsyncCodeActivity for doing fairly simple things – like WriteLine, HttpRequest, OK. But both of those guys are strictly more limited than what you can do with NativeActivity. When do you know you need to use NativeActivity? Maybe you want to…

  • Implement custom control flow - which requires scheduling activities
  • Evaluate workflow Expressions - which requires scheduling activities
  • Create an idle point where your workflow can be persisted which requires using bookmarks.
  • Set or get execution properties
 
Basic example: Scheduling a Child Activity

public class InfiniteLoop : NativeActivity

{

    public Activity Body { get; set; }

    protected override void Execute(NativeActivityContext context)

    {

        context.ScheduleActivity(Body, OnBodyCompleted); //*

    }

    void OnBodyCompleted(NativeActivityContext context, ActivityInstance inst)

    {

        context.ScheduleActivity(Body, OnBodyCompleted);

    }

}

 

This is the simplest possible way to schedule an activity. (Almost. Even simpler if we don’t provide a CompletionCallback.) What does this do? Loops forever. Simple enough.

 

Question: Why doesn’t this code stack overflow?

 

Answer: ScheduleActivity just schedules an activity. The scheduled activity (and completion callback) don’t actually execute until the current function (e.g. Execute() ) returns.

 

 

Framework example: The If activity and the While activity

 

public sealed class If : NativeActivity

{

    // …

    public Activity Else { get; set; }

    public Activity Then { get; set; }

    public InArgument<bool> Condition { get; set; }

}

public sealed class While : NativeActivity

{

    // …

    public Activity Body { get; set; }

    public Activity<bool> Condition { get; set; }

    public Collection<Variable> Variables { get; }

}

 

 

 

Here’s something a little interesting. Both an If activity and a While activity have a Condition property. But they are not implemented the same way. Why? The answer is scheduling control. In the implementation of the While activity, it is necessary to control exactly when Condition is being evaluated in relation to the while loop’s Body. It does something like this:

 

in Execute():

 

context.ScheduleActivity<bool>(Condition, OnConditonComplete);

 

in OnConditionComplete():

 

context.ScheduleActivity(Body, OnBodyComplete);

 

What we learn here is how to easily schedule or evaluate workflow expressions: ScheduleActivity<bool> is a function that allows an easy way of getting the Result value of an expression. The result will be passed in to the CompletionCallback<bool> function OnCoditionComplete.

 

 

[Infrequently Asked Question: what if I want the result, but with loose typing? (from an ActivityWithResult)

 

Answer: context.ScheduleActivityWithResult() might not be in the 4.0 framework release. In a pinch, you can use reflection.]

 

 

Harder still - parallel scheduling:

public class TwoChildren: NativeActivity

{

    public Activity FirstChild { get; set; }

    public Activity SecondChild { get; set; }

    protected override void Execute(NativeActivityContext context)

    {

        context.ScheduleActivity(FirstChild, OnChildCompleted); //*

        context.ScheduleActivity(SecondChild, OnChildCompleted); //*

    }

    void OnChildCompleted(NativeActivityContext context, ActivityInstance inst); //print which child completed

}

 

Question: What does this code do?

 

Answer: Argh! Not what it first looked like to me! It’s going to depend a lot on what FirstChild and SecondChild are.

 

Point 1 – scheduling children for execution is LIFO. Last child scheduled is first child in line for the chop (execution)! With a simple example you will probably see this output:

 

*Child2Completed*

*Child1Completed*

 

Point 2 – these children run in parallel. Even though SecondChild will begin executing first, it is possible that FirstChild will complete before SecondChild. A trace would might something like this:

 

*Child2Started*

*BookmarkCreatedInChild2*

*Child2GoesIdle*

*Child1Started*

*Child1Completed*
*BookmarkResumedInChild2*

*Child2Completed*

 

Point 3 – in Point 2 parallel doesn’t mean multithreaded. It just means unspecified execution order.

 

Question: What if I really wanted those two activities to execute in Sequence? FirstChild then SecondChild.

 

Answer: Don’t schedule SecondChild for execution until FirstChild has completed.