Note: This blog post is written using the .NET framework 4.0 Beta 2

In this previous blog post I showed how to create an asynchronous activity using the NativeActivity and CreateBookmark to pause a workflow execution. Using that in a WorkflowApplication was easy but what about WorkflowInvoker or WorkflowServiceHost?

 

So what about using a WorkflowInvoker or a WorkflowServiceHost?

Neither the WorkflowInvoker not the WorkflowServiceHost contain a ResumeBookmark function so how do we resume a bookmark using either of those execution hosts? The trick is to use a IWorkflowInstanceExtension.

 

Using a IWorkflowInstanceExtension

Workflow extensions can be of any type you want, there is no base class or interface requirement. But that also means they are not aware of the workflow runtime environment and can’t do much more that respond to calls from a workflow. The way do be able to do a little more is by implementing the IWorkflowInstanceExtension interface. This interface has just two methods. One of those, the SetInstance, is passed a wrapper object around the running workflow allowing us do do some more work. The most important is that this also lets us resume bookmarks.

My workflow extension basically waits for the activity to call it and once done wait one second and resume the bookmark. A

class MyExtension : IWorkflowInstanceExtension
{
    private WorkflowInstanceProxy _instance;
    public IEnumerable<object> GetAdditionalExtensions()
    {
        return null;
    }
 
    public void SetInstance(WorkflowInstanceProxy instance)
    {
        _instance = instance;
    }
 
 
    public void WaitSome(Bookmark bookmark)
    {
        ThreadPool.QueueUserWorkItem(state =>
            {
                Thread.Sleep(1000);
                var ias = _instance.BeginResumeBookmark(bookmark, 42, null, null);
 
                var result = _instance.EndResumeBookmark(ias);
                Console.WriteLine("BookmarkResumptionResult: '{0}'", result);
            });
    }
 
}

Note that the GetAdditionalExtensions() function just returns null as we are not adding extra extensions.

 

The new version of the activity

With the required extension done we still need to make sure it gets added to the workflow runtime of choice. The easiest option is to let the activity itself do so in the CacheMetadata() function by using the AddDefaultExtensionProvider() function. The new activity looks like this:

public class MyBookmarkedActivity : NativeActivity
{
 
    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        base.CacheMetadata(metadata);
        metadata.AddDefaultExtensionProvider<MyExtension>(() => new MyExtension());
    }
 
    protected override bool CanInduceIdle
    {
        get { return true; }
    }
 
    protected override void Execute(NativeActivityContext context)
    {
        var bookmark = context.CreateBookmark("MyBookmark", BookmarkResumed);
        var extension = context.GetExtension<MyExtension>();
        extension.WaitSome(bookmark);
 
    }
 
 
    private void BookmarkResumed(NativeActivityContext context, Bookmark bookmark, object value)
    {
        Console.WriteLine("Bookmark resumed with '{0}'.", value);
    }
}

 

Now this is all we need to run the activity as it will automatically add the required extension:

WorkflowInvoker.Invoke(new MyBookmarkedActivity());

or when using a WorkflowServiceHost

var wsh = new WorkflowServiceHost(new MyBookmarkedActivity());
wsh.AddDefaultEndpoints();
wsh.Open();
Console.WriteLine("Listening");
Console.ReadLine();
wsh.Close();

And just in case you where wondering, neither of these last two use any configuration file.

 

Enjoy!

www.TheProblemSolver.nl

Wiki.WindowsWorkflowFoundation.eu