Doing synchronous workflow execution using the WorkflowApplication

The WorkflowApplication is a great way to execute your workflows in process. Usually the fact that the WorkflowApplication is asynchronous is a great thing but there are cases when a little more synchronous execution is nice. For example executing a workflow and updating the state of the user interface is much simpler when the WorkflowApplication.Run() doesn’t finish until all work is done.

The key to creating a synchronous WorkflowApplication is using its SynchronizationContext. Normally you set this to SynchronizationContext.Current so everything executes on the current UI thread. However this is still an asynchronous call and the Run doesn’t block.

Take this very simple workflow and its execution output.

var workflow = new Sequence()


{


    Activities = {


        new WriteLine(){ Text="Workflow is running"}


    }


};


 


var app = new WorkflowApplication(workflow);


 


Console.WriteLine("Before WorkflowApplication.Run()");


app.Run();


Console.WriteLine("After WorkflowApplication.Run()");

image

As you can see the Run() didn’t block and the message after the Run() was printed before the message from the workflow.

 

Executing the workflow synchronously

Making this workflow execute in a synchronous fashion is easy and requires only a very small change by setting the SynchronizationContext to a custom implementation.

var workflow = new Sequence()


{


    Activities = {


        new WriteLine(){ Text="Workflow is running"}


    }


};


 


var app = new WorkflowApplication(workflow);


app.SynchronizationContext = new SynchronousSynchronizationContext();


 


Console.WriteLine("Before WorkflowApplication.Run()");


app.Run();


Console.WriteLine("After WorkflowApplication.Run()");

image

The SynchronousSynchronizationContext used is real simple. The workflow internals always call the Post() method to execute work, so all we need to do is just execute the delegate passed with the state passed.

class SynchronousSynchronizationContext : SynchronizationContext


{


    public override void Post(SendOrPostCallback d, object state)


    {


        d(state);


    }


}


Executing an asynchronous workflow

Using this same SynchronousSynchronizationContext  with an asynchronous workflow works just fine. If I add the following simple bookmarked activity to the workflow and execute it I get the following result:

image

The code to execute the workflow is as follows

var workflow = new Sequence()


{


    Activities = {


        new WriteLine(){ Text="Workflow has started"},


        new MyBookmarkedActivity(),


        new WriteLine(){ Text="Workflow is done"}


    }


};


 


var app = new WorkflowApplication(workflow);


app.SynchronizationContext = new SynchronousSynchronizationContext();


app.Idle = e => Console.WriteLine("WorkflowApplication.Idle called");


 


Console.WriteLine("Before WorkflowApplication.Run()");


app.Run();


Console.WriteLine("After WorkflowApplication.Run()");


Console.WriteLine();


Console.WriteLine("Before WorkflowApplication.ResumeBookmark()");


Console.WriteLine("ResumeBookmark: {0}", app.ResumeBookmark("MyBookmark", null));


Console.WriteLine("After WorkflowApplication.ResumeBookmark()");

 

And the bookmarked activity is as follows:

class MyBookmarkedActivity : NativeActivity


{


    protected override bool CanInduceIdle


    {


        get { return true; }


    }


 


    protected override void Execute(NativeActivityContext context)


    {


        Console.WriteLine("Creating bookmark");


        context.CreateBookmark("MyBookmark", BookmarkResumed);


    }


 


    private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value)


    {


        Console.WriteLine("Bookmark resumed");


    }


}






Conclusion



A simple yet effective addition to the workflow runtime for that special case where the asynchronous behavior is not quite what you want but the WorkflowInvoker is not flexible enough

















 



Enjoy!



[f1]
[f2]

3 thoughts on “Doing synchronous workflow execution using the WorkflowApplication

  1. There is one small problem. If the workflow aborts, the WorkflowApplication.Abort delegate will be invoked after the synchronization context completes – this is a subtle timing change but it could affect your error handling

  2. Yes the Aborted callback is the odd one that doesn’t use the SynchronizationContext specified. The same problem arises with a WPF application when you use the SynchronizationContext.Current and the Aborted callback still needs code to marshalling code to update the UI.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>