More asynchronous work in Windows Workflow Foundation 4

In my previous post I showed how to use an AsyncOperationBlock to do some basic asynchronous work in WF 4. As I mentioned this was for relatively short lived asynchronous operations as the workflow could not be persisted and unloaded while this asynchronous work was executing.

So how about real long running work, the kind you run into with real business application like waiting for an invoice to be paid?

 

With these king of jobs an AsyncOperationBlock will not work because you don’t want to keep the workflow in memory all the time.

So what do we need to use here?

Well the basic building block is called a Bookmark.

 

A bookmark is just a way of saying to the workflow: give a pointer to where you are and I will tell you later to resume from this pointer.

 

Creating a bookmark is easy. In the execute method of our activity we call the CreateNamedBookmark() function of the ActivityExecutionContext passed to us with the name of the bookmark. The bookmark name is no longer an IComparable but just a regular string.

Whenever we are done with our job we can use the WorkflowInstance to call ResumeBookmark() passing in the name of the bookmark and some data. The activity will be signaled as closed and we are ready to go.

One big difference is the activity base class. In the previous sample I was using the CodeActivity as the baseclass for my custom activity while in this case I am using the more powerful NativeActivity<T>.

A word of warning here, these are the names in WF4 Beta 1 and they are likely to change before WF4 is release!

 

So what does my activity look like?

class AsyncWorker : NativeActivity<String>


{


    protected override void Execute(ActivityExecutionContext context)


    {


        Console.WriteLine("CreateNamedBookmark on thread\t{0}", Thread.CurrentThread.ManagedThreadId);


        context.CreateNamedBookmark("bm");


    }


}

It prints a bit of status info and most importantly it creates a new named bookmark called “bm”.

 

The main program looks like this:

var workflow = CreateWorkflow();


var instance = new WorkflowInstance(workflow);


instance.OnIdle = () =>


{


    Console.WriteLine("Idle on thread\t\t\t{0}", Thread.CurrentThread.ManagedThreadId);


    return IdleAction.Nothing;


};


instance.OnCompleted = (e) => { Console.WriteLine("Completed on thread\t\t{0}", Thread.CurrentThread.ManagedThreadId); };


 


instance.Run();


 


Console.WriteLine("Sleep on thread\t\t\t{0}", Thread.CurrentThread.ManagedThreadId);


Thread.Sleep(1000);


 


Console.WriteLine("ResumeBookmark on thread\t{0}", Thread.CurrentThread.ManagedThreadId);


instance.ResumeBookmark("bm", null);


 


Console.WriteLine("Sleep on thread\t\t\t{0}", Thread.CurrentThread.ManagedThreadId);


Thread.Sleep(1000);


 


Console.WriteLine("Done on thread\t\t\t{0}", Thread.CurrentThread.ManagedThreadId);


Console.ReadLine();

If you strip out the printing of the status info you are basically left with:

var workflow = CreateWorkflow();


var instance = new WorkflowInstance(workflow);


instance.Run();


instance.ResumeBookmark("bm", null);



 



One interesting thing I noted is that is appears to be ok to resume a bookmark before it is created.



 



Enjoy!



[f1]
[f2]

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>