Workflow Foundation 4 allows you to add your own custom activities in code. You have a choice of CodeActivity, AsyncCodeActivity and NativeActivity. CodeActivity is ideal if you want to add a simple activity that doesn’t block. AsyncCodeActivity is great if you need an activity that needs to do some lengthy processing and you can support the IAsyncResult pattern. But if you require some lengthy business process that needs to wait for some external input you need a NativeActivity and a Bookmark.

Most samples you can find use the main method of the workflow host to send the required input for the bookmark, but this puts the burden on the host; it is way better to use an activity with an extension. The activity can then delegate the long work to the extension. In this post I will build a simple activity that uses an extension like this.

This activity works the way you find in a lot of samples. Also note that a NativeActivity that used bookmarks needs to override the CanInduceIdle property to return true. The idea is that in the host you wait for the workflow to go idle, then code it so you ask the user for the name, and then you resume the bookmark. Let’s NOT do this, but instead use a workflow extension.

Add another class called GetNameExtension, implementing the IWorkflowInstanceExtension interface:

1:publicclass GetNameExtension : IWorkflowInstanceExtension

2: {

3:private WorkflowInstance Instance { get; set; }

4:

5:publicvoid GetName(Bookmark bookmark)

6: {

7: ThreadPool.QueueUserWorkItem(

8: (ignore) =>

9: {

10: Console.Write("Your name please: ");

11:string name = Console.ReadLine();

12: Instance.BeginResumeBookmark(bookmark, name,

13: (ticket) => { Instance.EndResumeBookmark(ticket); }, null);

14: }

15: );

16: }

17:

18:public IEnumerable<object> GetAdditionalExtensions()

19: {

20:yieldbreak;

21: }

22:

23:publicvoid SetInstance(WorkflowInstance instance)

24: {

25: Instance = instance;

26: }

27: }

This class has a simple GetName method, taking the bookmark as an argument. It then starts some background processing to perform a lengthy operation, in this case getting some input from the console. Once it has the input it resumes the bookmark. The GetAdditionalExtensions is implement to return an empty collection (using yield break) and the SetInstance is implemented to store the current workflow instance (although not really required for this example).

Now let’s use the extension. The execute method should now be implemented to retrieve this extension, and then call its GetName method with the bookmark:

This way, when the activity gets created, it can install the extension itself.

Using an extension this way is ideal if you have a number of activities that need to talk to some backend system like a database. The extension can then be used to model the connection, while the activity itself models the command. All activities in the workflow instance then share the command…

Don’t worry, the workflow runtime will ensure only one instance of this extension is created for the workflow.