DCSIMG
Workflow External Data Exchange Service | ExternalDataExchange - Guy Burstein's Blog

Guy Burstein's Blog

Developer Evangelist @ Microsoft

News

Guy Burstein The Bu

Disclaimer
Postings are provided 'As Is' with no warranties and confer no rights.

Guy Burstein LinkedIn Profile

TwitterCounter for @bursteg

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:

External Data Exchange Service

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:

  1. The constructor receives a Guid instance ID and calls the base class constructor with thie ID as parameter.
  2. 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:

  1. Select the HandleExternalEvent activity from the toolbox and drag and drop it onto the workflow design surface. Set the name property of the activity.
  2. 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.
  3. Choose an event to handle from that interface, by choosing an item from the drop-down list of the EventName property of the activity.
  4. 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;
  5. 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:

  1. Select the CallExternalMethod activity from the toolbox and drag and drop it onto the workflow design surface. Set the name property of the activity.
  2. 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.
  3. Choose the method to call from that interface, by choosing an item from the drop-down list of the MethodName property of the activity.
  4. 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:

  1. Drag a CallExternalMethod activity or HandleExternalEvent activity to the design surface and choose the InterfaceType and EventName / MethodName.
  2. Click the […] button of a parameter / return value / event arguments.
  3. 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.

External Data Exchange Service - Parameter Binding

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!

Comments

Guy Burstein's Blog said:

wca.exe - Workflow Communication Activities Generation I have already written about External Data Exchange

# July 24, 2007 12:00 PM

Patrick said:

"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."

When I attempt this step my Interface does not show in the dialog. My interface is in another class Library. I have added the ExternalDataExchange attribute. The Workflow project references the class library with the ExternalDataExchange.

If I manually change the designer file and add the code to reference the Interface Type then everything works fine.

If I click on the [...] button again the interface is still not there.

Any help would be appriciated.

# January 22, 2008 8:02 PM

William said:

What if I have two classes of ExternalDataExchangeServices? From your example, say ActionCommunicationService1() and ActionCommunicationService2() each being used by two different types of workflows within the same runtime. How do I add both these classes?

# April 2, 2008 3:16 PM

rakesh said:

I am doing something as below

[ExternalDataExchange]

public interface IEvent

{

event EventHandler<ExternalDataEventArgs> EventAction;

}

I have created state machine as below

2 states as Unlocked and Locked.

In Unlocked state I have created event driven activity as LockedEvent.

 In LockedEvent I have created HandlerExternalEventActivity as HandleLockEvent and assigned interfaceType as IEvent.

 Then I added codeActivity from toolbox below LockedEvent

  Then I added SetStateActivity from toolbox and set TargetStateName to Locked .

In Locked state I have created event driven activity as UnlockedEvent.

 In UnlockedEvent I have created HandlerExternalEventActivity as HandleUnlockEvent and assigned interfaceType as IEvent.

 Then I added codeActivity from toolbox below LockedEvent

  Then I added SetStateActivity from toolbox and set TargetStateName to Unlocked .

Now I created a class as

[Serializable]

public class Locker3 : IEvent

{

#region IEvent Members

public event EventHandler<ExternalDataEventArgs> EventAction;

#endregion

public void ChangeState(Guid instanceId)

{

if (this.EventAction != null)

{

this.EventAction(this, new ExternalDataEventArgs(instanceId));

}

}

}

Now in program.cs I wrote following code

WorkflowRuntime workflowruntime = new WorkflowRuntime();

Guid locker_ID = Guid.NewGuid();

Dictionary<string, object> Params = new Dictionary<string, object>();

ExternalDataExchangeService ExDateExchService = new ExternalDataExchangeService();

workflowruntime.AddService(ExDateExchService);

Locker3 l3 = new Locker3();

workflowruntime = new WorkflowRuntime();

ExDateExchService.AddService(l3);

WorkflowInstance wfPocinstance = workflowruntime.CreateWorkflow(

typeof(POCWorkflow), Params, locker_ID);

wfPocinstance.Start();

l3.ChangeState(locker_ID);

Console.ReadLine();

I am getting exception as

Event "EventAction" on interface type "StateWorkFlow.IEvent" for instance id "bd668543-b8ee-4394-b3ae-dbfaa3ccd2ee" cannot be delivered.

Plese help

# May 14, 2008 5:39 PM

Guy Burstein said:

Hi,

Go to this line:

this.EventAction(this, new ExternalDataEventArgs(instanceId));

and replace "this" will "null".

Hope this helps,

Guy

# May 15, 2008 4:02 AM

Juan Pablo said:

Guy: is correct to implement all code activities in another dataexchange service?

My state machine workflow has a few activities (that implements que Activity base class) with his own code under "ActivityExecutionStatus Execute(...)". Yesterday I migrated all these code to a new DataExchangeService:

WorkflowActivitiesMethods

The idea behind this is to separate the logic of the Workflow dll, so modifications to the workflow code logic now depends on this class. The problem could become because the system runs under ASP.NET (with less thread pool), so don't know if this solution is the best. In my activities now I have something like:

WorkflowActivitiesMethods gateway = executionContext.GetService<WorkflowActivitiesMethods>();

gateway.Invoquemethod();

return ActivityExecutionStatus.Closed;

Greetings and sorry for my poor english!

# August 5, 2008 11:19 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 


Enter the numbers above: