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:
- Part I: Simplest communication case: Communication Host -> Workflow by parameters
- Part II: Intercommunications Workflow -> Host through CallExternalMethod Activity
- Part III: Intercommunications Host -> Workflow through HandleExternalEvent Activity
- Part IV: Organisation of the communication classes: Communication manager, wca.exe utility, and Wwca.exe Windows front-end for wca.exe
- Part V: Intercommunication with a Workflow instance using the Correlation parameter
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.
在Part I讲述当WorkFlow启动时接收参数。当不需要从workflow返回数据时这种方法没问题。但是,workFlow经常要返回处理的数据。
The Workflow foundation is delivered with an Activity component to do this work: the CallExternalMethod
Activity.
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).
在Part I的基础上,现在要从主程序输出数据。
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.
CallExternalMethod 把信息从Workflow 传给主程序。但是需要创建一个特殊的服务
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.
需要创建服务接口和功能,在WorkFlow runtime instance中注册该服务。
You can implement the communication as you want, by poll or by events, but you must fulfill the CallExternalMethod
requirements.
可以通过poll(??)或者事件的方式实现通信。
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. 由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. 一个类完成上述接口。由CallExternalMethod调用的方法要在该类中实现。在这个方法中,信息传向主程序。 - 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. 需要创建一个ExternalDataExchangeService 类,并在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: 在WorkFlow中
- You must have a
CallExternalMethod
Activity. 需要有CallExternalMethod Activity - In it, register the Communication Interface and the method to call. 在CallExternalMethod Activity中注册接口和方法。
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. CallExternalMethod 调用了另一个线程中的方法,当此方法执行后,
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. 定义又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. 该接口必须有一个特殊的头部定义,表示由workflow可调用的The complete code is shown here:
Collapse Copy Code///<summary> /// You MUST to mark the interface with the header ExternalDataExchange 必须用ExternalDataExchange标示此接口 /// to identify the interface as the communication interface. /// </summary> [ExternalDataExchange] public interface ICommunicationService { /// <summary> /// This method must be call by the CallExternalMethod Activity /// to transfer the information to the console host application /// <param name="response">Data to send to console</param > 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. 在此类中可以决定采用poll的方式或者event的方式处理数据。
In our code, we implement an event. We should pass the data from the Workflow in the arguments of the event:
Collapse Copy Code/// <summary > /// Implement the interface /// This implementation is made to comunicate WF -> Host /// </summary > public class CommunicationService: ICommunicationService { /// <summary > /// Event to communicate the host the result. /// </summary > public event EventHandler <SendDataToHostEventArgs> SendDataToHostEvent; /// <summary > /// Implement the external Method /// </summary > /// <param name="response" >response to host</param > 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: 下面是事件参数的定义
Collapse Copy Codepublic class SendDataToHostEventArgs: EventArgs { string _response = string.Empty; /// <summary > /// This property is used to pass in the Event /// the response to host /// </summary > 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. 在主程序里的workflow runtime instance中注册ExternalDataExchangeService. 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:
//Add support to the Communication Service..................
//Declare a ExternalDataExchangeService class
ExternalDataExchangeService dataservice = new ExternalDataExchangeService();
//Add to workflow runtime
workflowRuntime.AddService(dataservice);
//Declare our CommunicationService instance
CommunicationService cservice = new CommunicationService();
//Add to the ExternalDataService
dataservice.AddService(cservice);
//Add a handler to Service Event
cservice.SendDataToHostEvent += new
EventHandler<senddatatohosteventargs> (cservice_SendDataToHostEvent);
// end of support to Communication Service.................
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: 在workflow中添加CallExternalMethod activity
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:Collapse Copy Code[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.