Introduction
This is the second of five articles about Host Workflow communication.
In this series of articles, I will try to show the different possibilities to implement communication, from the simplest case to the more complex. I am not planning to be exhaustive, but I will try to give a general panoramic in order to get us a good idea about the thematic.
Because I also don’t like large articles, I have divided this series into the followings parts:
Background
In Part I, we understood how to pass information to a Workflow when it is launched. That works OK when you don’t need to return data from the Workflow. But often, you need to return information about the processed data in the Workflow.
The Workflow foundation is delivered with an Activity component to do this work: the CallExternalMethod
Activity.
Returning to the example of Part I, our program reads a file from the computer and sees determinate number of chars from it. In this program, we want to pass two parameters (the file path and the number of characters to return from the file), and the Workflow will show the information in an internal message box. Now, we have the requirement to show the return information directly in the console output and not in an internal message box form (see Figure 1).
You can see that the barrier between the threads is passed with the Workflow invocation and again to return the character from the file to the console host application.
To implement passing the information form the Workflow to the Host, WWF comes with a dedicated activity named CallExternalMethod
. That is the good news; the bad news is that we must create a specific service pattern to communicate with this activity.
The communication service pattern works as another service. You must create a service interface, implement the service, and register it in the Workflow runtime instance. You can implement the communication as you want, by poll or by events, but you must fulfill the CallExternalMethod
requirements. In short, you need to implement the following code structure:
Create the followings interface and classes:
- An interface with the method definition called by the
CallExternalMethod
Activity. - A class that implements the interface. In this class, you implement the method that is called by
CallExternalMethod
. In this method, you made the code to send the information to the host. - If you want to use Events to drive the communication, you should implement a specific
EventArgs
derived class to hold the information that you want to send to the host.
In the Host application, the following steps need to be taken:
- You must create a instance of the
ExternalDataExchangeService
class and register it in the Workflow runtime. - You must register your created Communication Service in this data service class.
- If you use events to drive the communication, you must also handle the communication event in your host class.
In the Workflow:
- You must have a
CallExternalMethod
Activity. - In it, register the Communication Interface and the method to call.
You can see the result class structure for our example in the following diagram:
You can see that the CallExternalMethod
makes a call to a method that is in another thread. When this method is executed, we have the results in the host thread! That is the trick.
We use a console application and a sequential Workflow to explain in detail how to create these structures.
Code Hands On
The best thing to do is download the code that is attached to this article, or if you have it from of Part I of this series, you can reuse it.
The companion project has the communication with the Workflow through parameters, that was explained in Part I. I assumed that you would know how to do it (if you don’t know, please revisit Part I of this article series).
We will now do a step by step explanation of the use of CallExternalMethod
in our example:
A. Create the followings interface and classes
- An interface with the method definition called by the
CallExternalMethod
Activity. You can create this interface in the same project as your host or in an independent project. For simplicity, we created it in the same host application. The interface simply contains the method to be called by the Workflow. You should now be thinking, why not call any method in the host? The answer is, this interface must have a special header to define it as an external method callable by the Workflow. The complete code is shown here:
[ExternalDataExchange]
public interface ICommunicationService
{
void SendDataToHost(string response);
}
Observe the ExternalDataExchangeHeader
. It determines that the method in the class can be called from the Workflow.
- A class that implements the interface.
The code for this class is your responsibility and your decision. You can decide to implement a method to simply hold the data passed from the Workflow and then the host will poll the class instance, or you can decide if you want to implement an event to asynchronously get the data.
In our code, we implement an event. We should pass the data from the Workflow in the arguments of the event:
public class CommunicationService: ICommunicationService
{
public event EventHandler <SendDataToHostEventArgs> SendDataToHostEvent;
public void SendDataToHost(string response)
{
SendDataToHostEventArgs e = new SendDataToHostEventArgs();
e.Response = response;
EventHandler<senddatatohosteventargs > sendData = this.SendDataToHostEvent;
if (sendData != null)
{
sendData(this, e);
}
}
}
You can see the implementation of the event and the simple code of the method above. The method only passes the response input parameter to the event arguments and raises the event.
The definition of the event argument is trivial, and is shown here:
public class SendDataToHostEventArgs: EventArgs
{
string _response = string.Empty;
public string Response
{
get
{ return _response; }
set
{ _response = value; }
}
}
In our example, we create the three classes in different files. You can see them here:
- SendDataToHostEventArgs.cs
- CommunicationService.cs
- ICommunicationService.cs
B. Register the Communication Service as ExternalDataExchangeService
We register it in the Workflow runtime instance in the host program. The intercommunication class Workflow-Host must be registered as an External Data Exchange Service in the runtime. The registration is relatively simple, as you can see in the following code:
ExternalDataExchangeService dataservice = new ExternalDataExchangeService();
workflowRuntime.AddService(dataservice);
CommunicationService cservice = new CommunicationService();
dataservice.AddService(cservice);
cservice.SendDataToHostEvent += new
EventHandler<senddatatohosteventargs> (cservice_SendDataToHostEvent);
The first four instructions are related to registering the intercommunication service, and the last to registering the event to communicate the result from the Workflow. Our application is now complete in the host side. Now we should make our Workflow.
C. In the Workflow…
Well, we use as base the same Workflow as in Part I. We suppress the code activity that shows the MessageBox
. Then, drag and drop a CallExternalMethod
activity to the Workflow, and configure it as in the following figure:
Note that the InterfaceType
property is filled with the created ICommunicationService
. The MethodName
is the method to be called by the activity in the interface. When you enter the MethodName
, the property is expanded and you must declare the variable that will fill the response parameter. In our application exists a variable _response
. To match the parameter with the variable, click in the ellipsis button in the Response field, and in the opened dialog, click in “Bind to a new member", create the field, and enter the name of the field _response
. (If you click in the example code, the binding is already created, and you can see the variable directly in the “Bind to an existing member” screen).
The rest of the logic is trivial, and you can see it in the example (the logic to access the file and pass the initialization parameters from the Host).
Compile the application and launch it. You can use your proper parameters to launch the application or use the default. (Maybe you do not have the specific file that is used as the default. Feel free to change it for another that you have).
Resumes
The basic step to pass information from the Workflow to the Host using the CallExternalMethod
Activity are the following:
Create the followings interface and classes:
- An interface with the method definition called by the
CallExternalMethod
Activity and decorated with the header:
[ExternalDataExchange]
A class that implements the interface. In this class, you implement the method that is called by CallExternalMethod
.
In the Host application:
- Register a instance of the
ExternalDataExchangeService
class in the Workflow runtime. - Add the instance of the communication class to
ExternalDataExchangeServiceInstance
. - If you use an event to drive the communication, you must also handle the communication event in your host class.
In the Workflow:
- Drop a
CallExternalMethod
Activity in the point of the Workflow that you want to send information to the Host. - In the
CallExternalMethod
Activity properties, register the Communication Interface, the method to call, and the variable or property to match the parameter of the called method.
Now, you know how to do a bi-directional communication between a host and a Workflow. But you will continue to have limitations. Until now, you only knew how to send information to a Workflow in the initialization phase, but what about sending information from the Host to the Workflow at any point of the Workflow? We will talk about this in the next part.
History
- First version - 03.10.2008.