This is another post in my WF 4.0: Custom Activities series. In a previous post I have talked about creating a code only custom activity that performs a simple task. In this post I’ll talk about creating a more complex activities that takes inputs from the calling program during their execution.
In the previous post WF 4.0: Code Only Custom Activities for Atomic Actions | CodeActivity, CodeActivity<T> I implemented the following activity for reading a string from the console.
public class ReadString2 : CodeActivity<string>
{
protected override void Execute(CodeActivityContext context)
string value = Console.ReadLine();
context.SetValue(Result, value);
}
While this activity will perform the task it is meant to do, it is blocking the calling thread and bad for scalability. In addition to that It is bound to getting the value from the console only, and it is not generic enough to support another input methods.
Enter Bookmarks.
To create a long running activity, add a new item of type Workflow Element item to the project. Notice that the default activity inherits from CodeActivity.
public class ReadString3 : CodeActivity
To create complex activities such as long running activities, me need to change the base class and inherit from NativeActivity or NativeActivity<T>. In addition to that, there is a difference between the type of the parameter that the CodeActivity takes for the Execute method and the type that NativeActivity takes. Change the input parameter type from CodeActivityContext to ActivityExecutionContext.
public class ReadString3 : NativeActivity
protected override void Execute(ActivityExecutionContext context)
throw new NotImplementedException();
In previous versions of Windows Workflow Foundation we had the ExternalDataExchangeService and WorkflowQueue when we wanted to build a simple long running activity. In WF 4.0 this is much more simple to do using Bookmarks.
To use bookmarks we need to:
The following activity registers a bookmark called “input” with a callback method called OnBookmarkCallback.
OutArgument<string> name;
public OutArgument<string> Name
get { return this.name; }
set
ThrowIfOpen();
this.name = value;
context.CreateNamedBookmark("input", new BookmarkCallback(this.OnBookmarkCallback));
void OnBookmarkCallback(ActivityExecutionContext context, Bookmark bookmark, object obj)
this.Name.Set(context, (string)obj);
In a previous post I have talked about the code that is needed to execute a workflow. Since we are now talking about long running workflows we will start with the default code that is generated for us when we create a new sequential workflow console application that uses the WorkflowInstance that lets know when the workflow instance is idle, and to resume a bookmark:
...
myInstance.Run();
//Get a string from the console and resume the bookmark called “input”
string input = Console.ReadLine();
myInstance.ResumeBookmark("input", input);
syncEvent.WaitOne(); ...
The main program performs the input and delivers the value to the workflow. Using this approach:
To query the available bookmarks in a workflow instance, we can use the following code:
IList bookmarks = myInstance.GetAllBookmarks();
foreach (BookmarkInfo info in bookmarks)
Console.Write(info.BookmarkName);
Enjoy!