



























This is the third of five articles about Host Workflow communication.
In this series of articles, I 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 the series in to the followings parts:
In Part II, we understood how to pass information to a Workflow when it is launched and how the workflow can return information to the host. 在第二部分讲述当workflow启动时把信息传递给workflow,以及workflow把信息返回给主程序。But, you can have a more complex situation, 考虑复杂一些的情况one that you must send information to the Workflow, not in the initialization phase but in an intermediate point, 可能再中间某个点给workflow信息,而不是在启动时,or you have to send information more often in the workflow's life time. 或者在workflow运行期间需要经常给workflow传递信息Also, you need to be able to send information in a determinate point of the Workflow flux. 或者在workflow的结束状态给workflow信息Those entire requirements can be fulfilled with the Activity HandleExternalEvent. 这些需求可以用Active HandleExternalEvent实现。
With HandleExternalEvent, you can stop the workflow and wait for a determinate external event, and when the event is received, the Workflow can continue the execution of the next activity.
使用HandleExternalEvent, 可以停止一个workflow等待一个特定的内部事件,当事件到来后,workflow能继续执行下面的动作。
HandleExternalEvent gets information from the Host in the Event Arguments parameter defined in the Event.
HandleExternalEnent 从主程序中定义的事件参数中获取信息
If we compare CallExternalMethod with HandleExternalEvent, we can see that both call a piece of software that is defined in another thread in another application. The first calls a method, and the second waits for an Event.

As you can see, you must supply the command or the number to the workflow each time the user clicks the Add button in the Host application. Obviously, you can not supply these information in the initialization of the Workflow.
…OK, you can run the Workflow each time you need the calculation; give the number, and the total is returned as a parameter, but I want to show you how to use the Workflow to control the process….
The HandleExternalEvent Activity waits for the defined external event and you are sure that the data is received at the right moment. HandleExternalEvent Activity 等待定义的外部事件The data is returned by using the CallExternalMethod that was explained in Part II of this article. 数据使用CallExternalMethod 返回主程序
To implement the communication, we need to fulfill a specific pattern that is similar to CallExternalMethod, but now, we need to define an external event. 这里需要定义一个外部事件In short, we need to do the following steps:
下面是需要做的工作
Create the following interface and classes:
CallExternalMethod activity 一个由CallExternalMethod调用的接口及方法and the External Event handled by the HandleExternalEvent activity. 由HandleExternalEvent 处理的外部事件CallExternalMethod. 在类中实现由CallExternalMethod调用的方法.In this method, you add the code to send the information to the host, and the event to be handled by the HandleExternalEvent activity. 通过方法中包括传送信息给主程序的代码和由HandleExternalEvent 处理的事件ExternalDataEventArgs. 外部事件的参数必须从一个Event Argument class:ExternalDataEventArgs继承。This special event argument comes with a constructor that needs the GUID from the instanced workflow that we want to communicate with. 这个事件参数有一个机构体,该结构体需要workflow的GUID。In our example, that is not very important because we only have an instance of the Workflow, but that plays an important role if we are working with different Workflows in the same time. 在本例中只有一个workflow实例,但如果同时有不同的workflow,GUID就很重要。CallExternalMethod in the host. 需要实现Event Argument Class用以接收由主程序CallExternalMethod 产生的信息。In the host application, do the following steps: 在主程序端需要的操作
ExternalDataExchangeService class and register it in the Workflow runtime. 创建ExternalDataExchangeService class 的一个实例,并在workflow runtime中注册。CommunicationCalculator and add it to the ExternalDataExchangeService instance (created in point 1). 创建一个CommunicationCalculator实例,并把该实例加到ExternalDataExchangeService instance 中(步骤1中建立)。In the Workflow: 在workflow中
CallExternalMethod activity. 使用CallExternalMethod activityHandleExternalEvent activity for each event that you want to handle to receive information from the host application. In this case, you need only one. 每一个事件必须对应一个aHandleExternalEvent activity 用于操作从主程序获取的信息。HandleExternalEvent activity with the declared communication interface and the event to call. Eventually, you can also define an event to be called after the external event is processed. 用声明的communication interface 和事件配置HandleExternalEvent activity 。此外还可以定义一个事件在external event 被处理后执行。CalculadorEventArguments to receive the information for the external event. 需要声明一个public 类型的CalculadorEventArguments用于接收external event的信息。In the following example, we have more code that we will explain here. That is because we don’t explain in depth what we need to for the functional logic for these examples, we concentrate only in the passing of the information.
You can see the result class structure for our example in the following diagram:

What is new here? The CalculadorEventArguments, as you see, makes a relation between two threads and it is all done through ICommunicationCalculator. CalculadorEventArguments 在两个线程之间起关联作用。The event to communicate data from the Workflow is raised in the host and is received from the HandleExternalEvent activity.
Now, we explain in detail how the program works.
To use the companion code, create an empty VS2008 solution and unzip the companion ZIP file in to the created project directory. In Visual Studio, add the three unzipped projects to the solutions. Build your solution.
The best part is downloading the code that is attached to this article. You can also developer your own ideas and copy and paste with creativity the code about the communication explained in the following paragraph.
The example project is a Windows project that adds integer numbers to a total field. You can add numbers, reset it, or exit from the program. You have three buttons to launch these functions.
In this project, we use an individual project to group all the classes that we need to implement the communication between the host and the workflow.
Create a project to hold the classes that you need to communicate the host with the workflow. Select a class project and name it WFCommunicationInterface. You can also revise the project in the attached code.
Open to add a file and select in New, the Item Interface item, then name it to ICommunicationCalculator. This file must have all the methods and events that are called or consumed by the workflow.
建立一个Interface, 命名为ICommunicationCalculator,包含由workflow调用的事件和方法
Add the following code to the class:
Collapse
Copy Code
[ExternalDataExchange]
public interface ICommunicationCalculator
{
#region Communication WF - > Host (explained in Part II)
void SendTotalToHost(Int64 total);
#endregion
#region Communication Host -> WF
event EventHandler<CalculadorEventArguments>SendCommandAndDataToWF;
#endregion
}
Here, you must define an event to do the communication between the host and the workflow. The EventHandler has a special EventArguments: CalculatorEventArguments. 必须定义一个用于host和workflow通讯的事件。EventHandler(事件委托)有一个特殊的事件参数CalculatorEventArguments
Now, create a new class file and add to the project. Name it as CalculatorEventArguments and create the following class:
下面完成CalculatorEventArguments的定义
Collapse
Copy Code
[Serializable]
public class CalculadorEventArguments: ExternalDataEventArgs
{
#region Create properties to hold the values to send to Workflow
string _command = "RESET";
int _valueToAdd = 0;
public string Command
{
get { return _command; }
set { _command = value; }
}
public int ValueToAdd
{
get { return _valueToAdd; }
set { _valueToAdd = value; }
}
#endregion
#region Create a Constructor with the InstanceId to pass to base class
public CalculadorEventArguments(Guid instanceID)
:base(instanceID)
{
}
#endregion
The event makes space for the information that we need to pass to the workflow: the command string and the integer to be added to the total sum. 这个事件定义了传给workflow的信息:command string 和一个integer(加到total sum 上)
Here, the two important points are the [serializable] attribute and the base class for the class. 注意两点:[serializable] 属性和基类的类型。You must decorate the class with the [serializable] attribute. This event argument must be used between threads in an inter communication process, and so it must be serializable.
The second point is the inheritance from ExternalEventArgs. 这个类从ExternalEventArgs继承。This special event argument has a constructor that take as parameter the GUID or the identifier for the workflow instance. 这个事件含有对workflow instance的标识You must use this identifier to guarantee that the information goed to the right instance of the Workflow. 使用此标识确保信息被传送到正确的workflow instance.The framework uses this parameter to derive the event to the correct instance of the workflow. 主程序使用此参数驱动事件传递给正确的workflow instance. You know that you can have multiples instances of the same workflow that run concurrently. We will see this in detail in another article.
You should also create an event to send to the host when the external method SendCommandAndDataToWp is executed. That is a normal event as you can see in the following code: 当外部方法SendCommandAndDataToWp执行后, 还需要建立向主程序发送的事件。这是一个普通事件
Collapse
Copy Code
public class SummeEventArgs: EventArgs
{
Int64 _summe = 0;
public Int64 Summe
{
get {return _summe;}
set {_summe = value;}
}
}
As you see, we use a normal argument to return the sum total from the workflow to the host. 使用一个普通的参数把sum值从workflow传递给host. In the last step, we need to implement the ICommunicationCalculator interface.最后需要完成ICommunicationCalculator 接口。Create a new file in the project, create a new class to create the file, and write the following code:
Collapse
Copy Code
public class CommunicationCalculator: ICommunicationCalculator
{
#region Zone Workflow to Host communication
public event EventHandler<summeeventargs > ReturnSummeEvent;
public void SendTotalToHost(Int64 total)
{
SummeEventArgs e = new SummeEventArgs();
e.Summe = total;
EventHandler<SummeEventArgs> sendData = this.ReturnSummeEvent;
if (sendData != null)
{
sendData(this, e);
}
}
#endregion
#region Zone Host to Workflow comunnication
public event EventHandler<CalculadorEventArguments> SendCommandAndDataToWF;
public void ReiseEventSendCommandAndDataToWF(Guid instanceID,
string command, int numberToAdd)
{
if (SendCommandAndDataToWF != null)
{
CalculadorEventArguments e = new CalculadorEventArguments(instanceID);
e.ValueToAdd = numberToAdd;
e.Command = command;
SendCommandAndDataToWF(null, e);
}
}
#endregion
}
We can analyze the code in parts. The first part has the communication between the workflow and the host that we showed in the second part of this series of articles. The second is new, but as you see is very simple to implement. You only need to define an instance for the external argument to be used. The workflow can automatically get this event and handle it.
The second procedure is a helper procedure to raise the event in the host. The use of this helper procedure encapsulates the call to the SendCommandAndDataToWF event.
As we see in the second part of this series of articles, you must register the communication service that we have created in step A as an ExternalDataService. 注册一个Communication service.
The code is quasi the same and you can see it here:
Collapse
Copy Code
#region Add support to the Communication Service
ExternalDataExchangeService dataservice = new ExternalDataExchangeService();
_workflowRuntime.AddService(dataservice);
cservice = new CommunicationCalculator();
dataservice.AddService(cservice);
cservice.ReturnSummeEvent +=
new EventHandler<summeeventargs> (cservice_ReturnSummeEvent);
#endregion end of support to Communication Service.....
As you can see, the code is the same as in the example in the previous article.
Here, in the click events for each command, each button fires a command in the workflow that we must transport to the workflow. To do this operation, we must raise the external event. See the following code in the buttons' Click events:
Collapse
Copy Code
#region Send data and commands to Workflow
private void bAdd_Click(object sender, EventArgs e)
{
if(IsValidNumber(tbNumber.Text))
{
cservice.ReiseEventSendCommandAndDataToWF(instance.InstanceId,
"ADD", int.Parse(tbNumber.Text));
}
}
private void bReset_Click(object sender, EventArgs e)
{
cservice.ReiseEventSendCommandAndDataToWF(instance.InstanceId, "RESET", 0);
}
private void bExit_Click(object sender, EventArgs e)
{
cservice.ReiseEventSendCommandAndDataToWF(instance.InstanceId, "EXIT", 0);
this.Close();
}
#endregion
We can see that it is simple calling the ReiseEventSendCommandAndDataToWF method from the communication interface. This method raises an external event in the workflow. The next section explain how to use this event.
Now, we need to configure the workflow application. In the following figure, we can see the implementation of the workflow to resolve the example task:

As you can see here, the sequence is simple, and we use the simple IfElse activities to select the right command and do the job.
Also, we use a While activity to wait for the command from the host and then execute it. The sequence ends when the Exit command is received. You can see the details of the implementation in the code.
You can see that the HandleExternalEvent Activity is directly after the beginning of the while loop. The HandleExternalEvent activity stops the flow of the workflow until the event is received. Then, we can be secure that the command and data are received at the right moment in the flow of the workflow. To configure the HandleExternalEvent activity is simple. The following figure shows how to configure it for our example.

As you can see, you need to declare the InterfaceType, the name of the event, and a variable that holds the information that the event from the Host gives us. You only need to declare a variable type CalculadorEventArguments, as you can see in the following code:
Collapse
Copy Code
public jagg.ClassCommunication.CalculadorEventArguments _returnedArguments =
default(CalculadorEventArguments);
Here, you can also see the property Invoked. You can use this property to invoke a method that is executed after the event is received. You can use this method to accommodate the received parameter in the right position in your workflow, or to do another task that you need. In our situation, we pass the received information to two internal variables:
Collapse
Copy Code
private void SetResponseInVariables(object sender, ExternalDataEventArgs e)
{
_numberToAdd = _returnedArguments.ValueToAdd;
_command = _returnedArguments.Command;
}
And that is all!
You can see the complete application in the attached code.
The basic steps to pass information from a Host to a Workflow using the HandleExternalEvent activity are:
Create the followings interface and classes:
HandleExternalEvent activity and decorated with the header: [ExternalDataExchange].HandleExternalEvent in the Workflow, and you can also implement the method that raises the event in the host application. In the Host application, do the following:
ExternalDataExchangeService class in the Workflow runtime.ExternalDataExchangeService instance.In the Workflow:
HandleExternalEvent Activity at the point of the Workflow that you want to get the information from the Host.HandleExternalEvent Activity properties, register the Communication Interface, the event to be consumed, and the variable or the property to hold the information returned by the event. Now, we have the basic tool to deal with the communication between a workflow and a host. We can resolve many problems that need to send information in a specific point of the workflow and return the information to the host. But all it is a little complex. You must register the service in one part of the program, and configure the HandleExternalEvent and CallExternalMethod classes. In the next part, we will cover the tool and a method to automatically generate a custom communication class and better organize the communication process.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。