原文地址:
http://www.codeproject.com/KB/WF/host_wf_comm_P4.aspx
到目前为止我们已经知道了Host和工作流交互的重点,现在我们要做一点小东西,使得我们的工作容易一些,如何用面向对象来组织我们的工作。
从前面的章节中我们看到,使用CallExternalMethod 和HandleExternalEvent活动来实现这些是可以的,但是如果我们有一个复杂的工作流,该使用那些活动可能就变得迷惑了。
workflow的SDK提供了一个自动添加所需CallExternalMethod 和HandleExternalEvent活动的工具,可以减轻我们的工作,使得工作流很容易理解。
wca.exe直接使用从dll中获取的必要信息来创建常用活动,dll是我们定义的外部交互接口。工具需要设置一些选项来生成常用行为。因为控制台程序通常比较复杂,因为数据的路径或者程序自己越来越复杂,作者写了一个小winform工具,可以免费获取。WWCA.ZIP
在使用这个小程序之前,要存储wca.exe的目录(可以参照C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin),在wwca.exe的配置菜单中设置。
如你看到的,工具只需要你的自定义交互服务的目录作为输入,他就会很容易的检查不同的输入参数和选项。
在前面我们开发的在Host中迁入工作流和交互服务没有任务的面向对象。那是因为我们的交互服务没有包装起来。
在这篇中,我们将创建一个交互管理类来驱动所有和交互有关系的。
好的,我们使用一个简单的程序控制一个简单的值。值可以被一个按钮驱动,另外一个按钮来驱动打开或者关闭这个值的状态。
有一条简单的业务规则,当值是关闭状态的话,值可以power off;如果值是打开状态 ,你不可以power off。
我们将使用一个状态机工作流来控制值得状态,用户的接口是一个winform的程序。
上图是这个程序的架构。
代码开发
1、交互管理器
从上面的图中我们可以看到Host是如何和工作流进行交互的 。
从工作流到Host:我们需要一个外部方法来返回状态信息给Host,你可以定义一些返回信息为字符串,例如,CLOSE OPEN EXIT INIT
从Host到工作流:这里你有两种选择,每个状态改变对应一个事件或者事件带一个新状态作为参数
我在这里选择每一个状态对应一个事件,这样我就不需要创建一个特殊的事件参数,我可以直接使用ExternalDataEventArgs 参数,因为我在参数中不需要传递自定义的信息。
我们需要声明三个事件,分别是:open,close,exit。不需要声明init,因为你创建工作流的时候你就是init。
首先创建下面的接口
public interface ICommunicationValve
{
#region Communication WF -> Host
/// <summary>
/// Used by CallExternalEvent Argument to pass the valve
/// status to Host
/// </summary>
/// <param name="status">Actual statis for valve</param>
void StatusValve(Guid wfGuid, string status);
#endregion
#region Communication Host -> WF
/// <summary>Use to pass the valve operation to workflow
///</summary>
event EventHandler<ExternalDataEventArgs> CloseValve;
/// <summary>Use to pass the valve operation to workflow
///</summary>
event EventHandler<ExternalDataEventArgs> Exit;
/// <summary>Use to pass the valve operation to workflow
///</summary>
event EventHandler<ExternalDataEventArgs> OpenValve;
#endregion
}//end ICommunicationValve
接下来要做的就是实现这个接口,因为我们这里是一个winform程序,因此我们需要在StatusValve 方法中实现一个内部的事件,通过事件传递信息给表单。
{
#region WF -> Host Communication
public event EventHandler<ExternalValveEventArgs>
EventValveStatus;
/// <summary>
/// Used by CallExternalEvent Argument to pass the valve
///status to Host
/// </summary>
/// <param name="status">Valve status OPEN / CLOSE / EXIT</param>
public void StatusValve(Guid wfGuid, string status)
{
if (EventValveStatus != null)
{
ExternalValveEventArgs e = new ExternalValveEventArgs(wfGuid);
e.Status = status;
EventValveStatus(this, e); //Raise the event
}
}
#endregion
#region Host -> WF
/// <summary>
/// Use to pass the valve operation to workflow
/// </summary>
public event EventHandler<ExternalDataEventArgs> CloseValve;
/// <summary>
/// Use to pass the valve operation to workflow
/// </summary>
public event EventHandler<ExternalDataEventArgs> Exit;
/// <summary>
/// Use to pass the valve operation to workflow
/// </summary>
public event EventHandler<ExternalDataEventArgs> OpenValve;
#endregion
#region Auxiliar procedures
/// <summary>
/// Raise the event Exit
/// </summary>
/// <param name="instanceId">workflow instance</param>
public void RaiseExit(Guid instanceId)
{
if (Exit != null)
{
ExternalDataEventArgs e = new ExternalDataEventArgs(instanceId);
e.WaitForIdle = true;
Exit(null, e);
}
}
/// <summary>
/// Raise the event Open
/// </summary>
/// <param name="instanceId">workflow instance</param>
public void RaiseOpen(Guid instanceId)
{
if (OpenValve != null)
{
ExternalDataEventArgs e = new ExternalDataEventArgs(instanceId);
OpenValve(null, e);
}
}
/// <summary>
/// Raise the event Close
/// </summary>
/// <param name="instanceId">workflow instance</param>
public void RaiseClose(Guid instanceId)
{
if (CloseValve != null)
{
ExternalDataEventArgs e = new ExternalDataEventArgs(instanceId);
CloseValve(null, e);
}
}
#endregion
}//end CommunicationValve
如你所看到的,我们直接在实现接口的类中包装了激发事件的方法,这样你就不需要在外部激发事件。
就像你在前面的章节中看到的,你需要在工作流运行时中注册这个服务为你的交互服务。那么创建一个包装这些活动的对象是一个好主意。这里我们创建一个交互管理类,这个类一定只能包含一个CommunicationValve 类的实例,一个工作流运行时的引用。
代码如下
{
/// <summary>
/// Single onject of the communicationValve class.
/// </summary>
private static CommunicationValve Comvalve = null;
/// <summary>
/// Reference to the workflow runtime.
/// </summary>
private WorkflowRuntime runtime = null;
/// <summary>
/// Return the CommunicationValve instance.
/// </summary>
public CommunicationValve Valve
{
get
{
return Comvalve;
}
}
/// <summary>Constructor Communication manager</summary>
/// <param name="wfRuntime">Runtime instance</param>
public CommunicationManager(WorkflowRuntime wfRuntime)
{
runtime = wfRuntime;
if (Comvalve == null)
{
Comvalve = new CommunicationValve();
}
}
/// <summary>
/// Procedure to register the communication service.
/// </summary>
public void RegisterCommunicationService()
{
//Declare a ExternalDataExchangeService class
ExternalDataExchangeService dataservice = new
ExternalDataExchangeService();
//Add to workflow runtime
runtime.AddService(dataservice);
//Add to the ExternalDataService
dataservice.AddService(Comvalve);
}
}//end CommunicationManager
我们可以应用一个假象概念,在这个管理类中声明一个触发ValveCommunication 类中事件的方法,而不是直接调用ComValve 实例。
我们已经有了自己的管理类,全部的类关系图如下图
2、应用工作流
创建一个状态机工作流库项目,我们要做的第一布就是建立自定义行为。
打开wwca.exe程序,选择在前面创建的dll,也就是CommunicationManager。选择输出工作路的路径,选择option中的include sender ,填写 在你的项目中使用的命名空间。
然后选择【action】-》【Execute wca】,如果操作正确的话,在下面的结果中就会看见内容,wca.exe发送的控制信息。