Communication between inside and outside of a workfow in windows workflow
October 18, 2008
In this blog, I will demonstrate an example of communication between outside of a workflow and the workflow. Let’s assume that a workflow consists of tasks. We need to send task information to outside of a workflow, and outside can send a result back into a workflow.
Basically, our workflow consists of tasks. There is nothing much going on in a task except that there is communication between a workflow and outside of a workflow. A workflow needs to inform the outside what task is, and the outside has to inform the workflow result of the task. We would like to group of these logics into a composite activity.
Mike Taulty provided an easy understanding screencast called, “Windows Workflow Foundation: Two-Way Host/Workflow Communication,” ”Windows Workflow Foundation: Workflow to Host Communication,” and “Windows Workflow Foundation: Host to Workflow Communication.” Source codes presented here are adapted from Mike Taulty’s screencasts.
1) Defining an interface for a local service
A local service is needed for communication. Please refer to this MSDN page, “Using Local Services in Workflows” for a better understanding of the local service. A local service runs independently outside of a workflow. If a workflow would like to send a message to outside, it sends through a local service. If an outside wants to send a message to a workflow, it sends through a local service. We need to define the interface containing a function which a workflow will call to outside and an event which outside sends an event to a workflow. We have defined an interface called ILocalService1. A local service is a class which will implement this ILocalService1 interface.
[ExternalDataExchange]
public interface ILocalService1
{
void distributeTask(int task_id, string workDescription, Guid workflowInstanceID);
event EventHandler<TaskEventArgs> taskEventArgsEventHandler;
}
The interface defines one function, distributeTask, and one event, taskEventArgsEventHandler.
- The distributeTask function is used by a workflow to send a message to outside of a workflow which can be done using the CallExternalMethod activity. Please refer to Tiago Oliveira blog, “Using WF CallExternalMethod activity in Windows Workflow” for a better understanding of the CallExternalMethod activity.
- The taskEventArgsEventHandler event is used when the outside wants to send a message to a workflow which can be done by the HandleExternalEvent activity. The TaskEventArgs class is used to contain data sent from outside. Following is the TaskEventArgs class.
[Serializable]
public class TaskEventArgs : ExternalDataEventArgs
{
public TaskEventArgs(Guid instanceId) : base(instanceId)
{
}
private int task_id;
public int Task_id
{
get { return task_id; }
set { task_id = value; }
}
private string status;
public string Status
{
get { return status; }
set { status = value; }
}
}
A workflow instance ID, instanceId, is needed for creating a TaskEventArgs object, so that an event is sent to the correct workflow. The TaskEventArgs class contains two fields: task_id and status. These two fields are data that we send into a workflow.
2) Creating a local service class by implementing an interface
Next, we need to to create a local service class by implementing the ILocalService1. The distributeTask function is fake. It does not distribute a task to a resource. It just sleeps for a period of time to stimulate that someone has performed a task.
[Serializable]
public class LocalService1 : ILocalService1
{
private int count = 5;
void ILocalService1.distributeTask(int task_id, string description, Guid workflowInstanceID)
{
ThreadPool.QueueUserWorkItem(delegate
{
count–;
Thread.Sleep(count * 15000); //sleep for a certain period of time
//create taskEventArgs and set its field
TaskEventArgs taskEventArgs = new TaskEventArgs(workflowInstanceID);
taskEventArgs.Task_id = task_id;
taskEventArgs.Status = “finished”;
if (taskEventArgsEventHandler != null)
{
taskEventArgsEventHandler(this, taskEventArgs); //Firing the taskEventArgsEventHandler
}
}
);
}
public event EventHandler<TaskEventArgs> taskEventArgsEventHandler;
}
The distributeTask function is called by a workflow. It sleeps for a certain period of time. It creates a taskEventArgs parameter which it will sent into a workflow when it firs an taskEventArgsHandler event.
3) Custom activity for communication between a workflow and outside
Our composite activity has to call the distributeTask defined in the local service, LocalService1, to send a task data to outside. When outside finishes performing a task, it sends back a task’s result through firing the taskEventArgsEventHandler event.
3.1 Go ahead and add a new activity into a workflow project.
3.1 Add four activites sequentially : codeActivity1, callExternalActivity1, handleExternalActivity1, and codeactivity2.
We would like to define two dependency properties as metadata for this composite activity. Please refer to my previous blog for creating a metadata.
3.2 Open the code view of this composite activity and add two dependency properties : Task_idProperty and WorkDescriptionProperty. A dependency property is used here as a metadata property for our custom activity. Metadata property can be edited only in design time, not in the runtime.
- Task_idProperty represents the task ID.
- WorkDescriptionProperty represents the work description of this particular task.
//class Activity1
…
public static DependencyProperty Task_idProperty = DependencyProperty.Register(
“Task_id”, typeof(System.Int32),
typeof(callexternal.Activity1),
new PropertyMetadata(DependencyPropertyOptions.Metadata) );
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute("Parameters")]
public Int32 Task_id
{
get
{
return ((int)(base.GetValue(callexternal.Activity1.Task_idProperty)));
}
set
{
base.SetValue(callexternal.Activity1.Task_idProperty, value);
}
}
public static DependencyProperty WorkDescriptionProperty = DependencyProperty.Register(
”WorkDescription”, typeof(System.String), typeof(callexternal.Activity1),
new PropertyMetadata(DependencyPropertyOptions.Metadata));
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)]
[BrowsableAttribute(true)]
[CategoryAttribute("Parameters")]
public String WorkDescription
{
get
{
return ((string)(base.GetValue(callexternal.Activity1.WorkDescriptionProperty)));
}
set
{
base.SetValue(callexternal.Activity1.WorkDescriptionProperty, value);
}
}
…
3.3 Add two fields to our custom activity class. They are tmpWorkflowInstanceID of type Guid representing a workflow’s instance ID and tmpTaskEventArgs of type TaskEventArgs representing an input from outside. This field will store a workflow instance ID which we can retrieve in the codeActivity1. It will be passed to outside when calling the distributeTask function in the LocalService1.
…
public static Guid tmpWorkflowInstanceID ;
…
public TaskEventArgs tmpTaskEventArgs = default(callexternal.TaskEventArgs);
…
3.4 Implementing the codeActivity1
- Type the function name which is “StartActivity” in the ExecuteCode textbox in the properties window of the codeActivity1. Press enter and the StartActivity function is generated.
The StartActivity function looks like following.
private void StartActivity(object sender, EventArgs e)
{
//initialize the workflow instance ID
tmpWorkflowInstanceID = this.WorkflowInstanceId;
Console.WriteLine(“Task : “ + this.Task_id+ ” is started of workflow ” + tmpWorkflowInstanceID);
Console.WriteLine(“Description : “ + this.WorkDescription );
}
3.5 Implement the callExternalActivity1
3.5.1 Select an interface implemented by a local service.
The callExternalActivity1 activity will call distributeTask function which has three parameters: int task_id, string description and Guid workflowInstanceID. These variable appeared on the properties window. We bind these varibles into fields of the custom activity which we have declared before in the 3.2 and 3.3 section. When binding, there are two things to set: name and path. Name is the parent object containing a field. In this case, it is the custom activity, Activity1. Path is the field in the parent object. Path refers to a field of the parent object. You can bind variables easily by clicking on a small button at each variable textbox in the property window.
3.5.2 Bind task_id to Task_id field.

Binding the task_id parameter of the function that the workflow calls to the Task_id field of the custom activity, Activity1
3.5.3 Bind workDescription to WorkDescription field.

Binding the workDescription parameter to the WorkDescription field of the custom activity, Activity1.
3.5.4 Bind workflowInstanceID to tmpWorkflowInstanceID.

Binding the workflowInstanceID parameter to the tmpWorkflowInstanceID field of the custom activity, Activity1.
3.6 Implement the handleExternalEventActivity1
When the local service fires an event, it passes TaskEventArgs object into a workflow, so we store it into a variable of the custom activity, Activity1, called tmpTaskEventArgs.

Binding an event argument passed from outside into a workflow to tmpTaskEventArgs of the custom activity, Activity1
3.7 Implement the codeActivity2
Finally, we just print out the values of fields here. Type “finishActivity” in the executeCode textbox in the property window of the codeActivity2 activity.
private void finishActivity(object sender, EventArgs e)
{
Console.WriteLine(“Task “ + this.Task_id + ” receives taskEventArgs “ + tmpTaskEventArgs.Task_id + ” : “ + tmpTaskEventArgs.Status );
}
4) Create a workflow
You need to build first. If the build is successful, the activity1 will be added into the toolbox of the workflow designer.
4.1 Open up the workflow in the workflow designer and drop the custom activity, Activity1. Add five of the activity1. A ParallelActivity is needed at the top. Then add SequenceActivity up to 5. Then add the custom activity, Activity1, into each SequenceActivity.
4.2 Remember that we have declared two metadata properties in the section 3.2 (Task_id and WorkDescription). Let’s initialize it by selecting a custom activity and typing a value in the property window as shown below.

Initialize the metadata properties (Task_id and WorkDescription) that we have declared as dependency properties of the custom activity, Activity1, before.
5) Add the ExternalDataExchangeService and the local service to the workflow runtime
In the main function, declare a new ExternalDataExchangeService object called dataService. Add dataService into the workflow runtime. Finally, add the local service class, LocalService1, into the dataService.
class Program
{
static void Main(string[] args)
{
ExternalDataExchangeService dataService = new ExternalDataExchangeService();
using(WorkflowRuntime workflowRuntime = new WorkflowRuntime())
{
workflowRuntime.AddService(dataService);
dataService.AddService(new LocalService1());//add local service
AutoResetEvent waitHandle = new AutoResetEvent(false);
workflowRuntime.WorkflowCompleted += delegate(object sender, WorkflowCompletedEventArgs e) {waitHandle.Set();};
workflowRuntime.WorkflowTerminated += delegate(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine(e.Exception.Message);
waitHandle.Set();
};
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(callexternal.Workflow1));
instance.Start();
waitHandle.WaitOne();
}
Console.ReadLine();
}
}
6) Run the workflow
You would see that the workflow works incorrectly. A TaskEventArgs for task 1 should go back to task 1. If you noticed, I have changed reverse the order that the local service will send an event back to the workflow.
Therefore, we need to use the correlation parameter.
7) Adding correlation parameter
The purpose of the correlation parameter is to identify an activity which outside sends a message to.
You can also read about the correlation parameter from the Programming Windows Workflow Foundation book (chapter eight). Please refer to ”Mike Taulty’s screencast on Correlation” for a quick tutorial on the correlation parameter.
7.1) Add Correlation Attributes to the Local Service Interface
The task_id parameter is chosen as the correlate parameter. Please remember that each custom activity, Activity1, has a unique Task_id field. The value in the Task_id field will be passed through the distributeTask function. Please refer to the MSDN page, “Using Correlation in Workflows,” for further information on correlation attributes: CorrelationParameter, CorrelationInitializer and CorrelationAlias.
[CorrelationParameter("task_id")]
[ExternalDataExchange]
public interface ILocalService1
{
[CorrelationInitializer]
void distributeTask(int task_id, string workDescription, Guid workflowInstanceID);
[CorrelationAlias("task_id", "e.Task_id")]
event EventHandler<TaskEventArgs> taskEventArgsEventHandler;
}
7.2) Set correlation token name
7.2.1 A Correlation Token is a string indicating a conversation between activities. A correlation token on both handleExternalEventActivity1 and callExternalMethodActivity1 activities are needed to be set to the same value, so both activities binded into the same conversation, and when a call is made from the callExternalMethodActivity1, the workflow runtime can return it to this handleExternalActivity1 in this custom Activity1, not other handleExternalActivity1 of other activities. In this case, we set the correlation token to be “tc1″ on both activities.
7.2.2 Run the application
Finally, the result is correct.
Project file : without correlation and with correlation.
Entry Filed under: Microsoft and .NET, Windows Workflow. Tags: correlation, correlation parameter, custom activity, local service, wf, Windows Workflow, workflow, wwf.
1 Comment Add your own
Leave a Comment
Some HTML allowed:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
Trackback this post | Subscribe to the comments via RSS Feed








1. Curious about the Correlation example in Microsoft Windows Workflow Foundation Step by Step book « Sukasom | October 20, 2008 at 9:33 am
[...] tasks running parallel in a workflow. Note, this example could be found in my previous blog, “Communication Between Inside And Outside Of A Workfow In Windows Workflow.” Each task sends a request and waits for response. Each task has uniquely Task ID. The [...]