Workflow External Data Exchange Service | ExternalDataExchange
Workflow External Data Exchange Service - ExternalDataExchange
In many scenarios, a workflow will need to communicate with the application in order to get some data, or notifications about state that has changed. In order to achieve that, the workflow needs a mechanism to exchange data with the application. Speaking in Windows Workflow Foundation terms, it is called External Data Exchange.
This post explains how to communicate between a workflow and the hosting environment using the External Data Exchange Service. After reading this series of posts, you will be able to understand each of the elements in the following Diagram:
Long running workflows contain activities that their execution depends on "user" actions after the workflow has already started. In those scenarios, the workflow is waiting for the application to raise an event before it continue to the next activity.
This is how you should implement such long running scenario using the External Data Exchange Service.
External Data Exchange Interface
The first thing you should do is to create an interface that will define the communication contract between the workflow and the application. This interface can contain methods and events and there are several rules for each of these elements. (These rules will be covered later in this post).
This is an example of a External Data Exchange Interface. This interface is decorated with the [ExternalDataExchangeAttribute], and in this case contains a single event.
[ExternalDataExchange]
public interface IExternalData
{
event EventHandler<ActionEventArgs> Done;
}
External Data Exchange Events and Arguments
Notice that the event declared in the above interface has a specific declarations type. The event uses the delegate EventHandler<T> that receives (object sender, T e) as arguments, where T is derived from EventsArgs class.
The event arguments class is used to communicate between the application and a workflow must derive from System.Workflow.Activities.ExternalDataEventArgs class. This class holds the woflow instance ID that the raised event is related to.
The ActionEventArgs that is used in the above interface is declared like this:
[Serializable]
public class ActionEventArgs : ExternalDataEventArgs
{
public ActionEventArgs(Guid instanceId, ...)
:base(instanceId)
{
...
}
...
}
There are two about that event arguments class you should notice:
- The constructor receives a Guid instance ID and calls the base class constructor with thie ID as parameter.
- This class is Serializable.
External Data Exchange Service
After creating the External Data Exchange Interface, you should create a class that implements it and attach it to the Workflow Runtime using the ExternalDataExchangeService.
The class you should create that implements the Extrenal Data Exchange Interface is the communication service. Use this code to add the External Data Exchange Service to the workflow runtime, and add your communication service as a service for exchanging data with workflows:
// Add the ExternalDataExchangeService to the WorkflowRuntime
ExternalDataExchangeService dataExchange = new ExternalDataExchangeService();
// Add the Communication Service as a service for exchaging data
workflowRuntime.AddService(dataExchange);
dataExchange.AddService(new ActionCommunicationService());
Handling External Event using HandleExternalEventActivity
Using the HandleExternalEventActivity blocks the workflow until the registered event is raised by the communication service.
In order to use the HandleExternalEventActivity and relate to a specific event of the communication service, simply follow the following steps:
- Select the HandleExternalEvent activity from the toolbox and drag and drop it onto the workflow design surface. Set the name property of the activity.
- Use the […] button of the InterfaceType property, and use the dialog to choose an External Data Exchange Interface that is declared in your project, or begin referenced by it.
- Choose an event to handle from that interface, by choosing an item from the drop-down list of the EventName property of the activity.
- The event sends a EventArgs instance as described in the previous post. In order to save It for later usage, declare a member:
// Workflow1.cs
public ActionEventArgs actionArguments = null;
- Set the e property of the activity. Use this property to inform the workflow in which member the received instance of EventArgs will be stored in.
To test this activity, I used the following code snippet. It registers the ExternalDataExchangeService to the runtime, and adds my communication service that implements the Extrenal Data Exchange Interface. Then, It starts a workflow instance. After waiting for somw time, It raises the Done event. After raising the event, the workflow will continue its execution.
// Add the ExternalDataExchangeService to the WorkflowRuntime
ExternalDataExchangeService dataExchange = new ExternalDataExchangeService();
workflowRuntime.AddService(dataExchange);
// Add the Communication Service as a service for exchaging data
ActionCommunicationService commSercice = new ActionCommunicationService();
dataExchange.AddService(commSercice);
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(Workflow1));
instance.Start();
Thread.Sleep(20000);
commSercice.DoYourWork(instance.InstanceId);
Some Important Tips
The above code snippet called the method DoYourWork of the communication service, that in some point raised the Done Event. Lets examine that code:
public void DoYourWork(Guid instanceID)
{
...
if (this.Done != null)
{
this.Done(null, new ActionEventArgs(instanceID, ""));
}
}
Keep in mind that there is a many-to-one relationship between the the communication service and the workflow instance. By that, I mean that a single communication service can communicate with many workflow instances. Therefore, you should always pass the workflow instance ID as parameter to methods, and also raise the event with the right instance ID. (Tip #1)
Notice that when calling the event handlers, you pass an object sender. (Tip #2) This objects must be serializable, or else, you will receive a System.Workflow.Activities.EventDeliveryFailedException with message like: "Event <YourEventName> on interface type <YourInterfaceName> for instance id <YourInsatnceID> cannot be delivered." With an inner exception message: "EventArgs not serializable". Although the problem is with the sender and not with the event arguments, this is the inner exception message. A bit confusing.
Using CallExternalMethodActivity
After creating a communication service and registering it to External Data Exchange Service, we could use the HandleExternalEvent activity in order to handle events of that service. In the opposite direction, we are able to call methods of the service, pass parameters and get return values.
If you are familiar with the HandleExternalEvent activity, using this activity is rather simple.
In order to use the HandleExternalEventActivity and relate to a specific event of the communication service, simply follow the following steps:
- Select the CallExternalMethod activity from the toolbox and drag and drop it onto the workflow design surface. Set the name property of the activity.
- Use the […] button of the InterfaceType property, and use the dialog to choose an External Data Exchange Interface that is declared in your project, or begin referenced by it.
- Choose the method to call from that interface, by choosing an item from the drop-down list of the MethodName property of the activity.
- After choosing the method name, the parameters of that method will appear in the property grid. You can specify constant values as parameters, or you can bind the parameters to a specific workflow field using ParameterBindings (explained later). If the method has a return value, set the field to store the return value in.
Parameter Binding
When passing parameters to methods, you can choose to set a constant value (MaxItems = 2), or you can bind the parameter value to some variable. For example, if the value is not known in design time and only in runtime. One good example for the usage in workflow scenarios is to handle a communication service event that sends some event arguments, and call a method with the ID of the entity that its event was handled.
To use Parameter Binding:
- Drag a CallExternalMethod activity or HandleExternalEvent activity to the design surface and choose the InterfaceType and EventName / MethodName.
- Click the […] button of a parameter / return value / event arguments.
- The Bind Property Dialog will open. You can use the first tab – "Bind to an existing member" to choose a public field or property to bind to the parameter, or you can use the other tab – "Bind to a new member" to create the member to create to store the value.
CallExternalMethodActivity.MethodInvoking and HandleExternalEventActivity.Invoked
What if you want to change the state of your workflow instance after handling a event? What if you want to prepare the parameter object before calling the external method?
In these cases, there couple of activity events that are very useful:
CallExternalMethod activity has a event called MethodInvoking that occurs just before the external method is called by the workflow. You can easily handle this event by double-clicking the activity or by typing a handler method name in the property grid of the activity and pressing Enter. This will create the following handler method:
private void CallExternalMethod1_MethodInvoking(object sender, EventArgs e)
{
}
HandleExternalEventActivity activity has a event called Invoked that occurs right after the external event is handled by the workflow. You can easily handle this event by double-clicking the activity or by typing a handler method name in the property grid of the activity and pressing Enter. This will create the following handler method:
private void HandleExternalEvent1_Invoked(object sender, ExternalDataEventArgs e)
{
}
Notice that the event arguments of this event is ExternalDataEventArgs , which is the base class of the Event arguments we used to create the events in the External Data Exchange Interface.
Summary
In this post I talked about the windows workflow foundation way of communicating between the workflows and the hosting environment - External Data Exchange.
I have shown how to declare an External Data Exchange Interface with External Data Exchange Events and how to register the External Data Exchange Service to the Workflow Runtime.
Then, I talked about how to use the HandleExternalEvent Activity and the CallExternalMethod activity, and how to bind parameters to members in your workflow class. I also explained how to use the CallExternalMethodActivity.MethodInvoking and HandleExternalEventActivity.Invoked events to extend the operation done by these activities.
You may want to read additional posts about Windows Workflow Foundation.
Enjoy!