代码改变世界

宿主和工作流:两个世界的交互 4(Host给工作流传递数据)

2008-11-26 09:03  Virus-BeautyCode  阅读(696)  评论(3编辑  收藏  举报

     原文地址:
      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 
      wf11.JPG
      在使用这个小程序之前,要存储wca.exe的目录(可以参照C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin),在wwca.exe的配置菜单中设置。
      如你看到的,工具只需要你的自定义交互服务的目录作为输入,他就会很容易的检查不同的输入参数和选项。
      在前面我们开发的在Host中迁入工作流和交互服务没有任务的面向对象。那是因为我们的交互服务没有包装起来。
      在这篇中,我们将创建一个交互管理类来驱动所有和交互有关系的。
      好的,我们使用一个简单的程序控制一个简单的值。值可以被一个按钮驱动,另外一个按钮来驱动打开或者关闭这个值的状态。
      有一条简单的业务规则,当值是关闭状态的话,值可以power off;如果值是打开状态  ,你不可以power off。
      我们将使用一个状态机工作流来控制值得状态,用户的接口是一个winform的程序。
      wf22.JPG
      上图是这个程序的架构。
      代码开发
      1、交互管理器
      从上面的图中我们可以看到Host是如何和工作流进行交互的 。
            从工作流到Host:我们需要一个外部方法来返回状态信息给Host,你可以定义一些返回信息为字符串,例如,CLOSE OPEN EXIT INIT
            从Host到工作流:这里你有两种选择,每个状态改变对应一个事件或者事件带一个新状态作为参数
      我在这里选择每一个状态对应一个事件,这样我就不需要创建一个特殊的事件参数,我可以直接使用ExternalDataEventArgs 参数,因为我在参数中不需要传递自定义的信息。
      我们需要声明三个事件,分别是:open,close,exit。不需要声明init,因为你创建工作流的时候你就是init。
      首先创建下面的接口

[ExternalDataExchange]
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 方法中实现一个内部的事件,通过事件传递信息给表单。

public class CommunicationValve : ICommunicationValve
{
  
#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 类的实例,一个工作流运行时的引用。
      代码如下
public class CommunicationManager 
{

  
/// <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 实例。
      我们已经有了自己的管理类,全部的类关系图如下图
wf33.JPG
2、应用工作流
      创建一个状态机工作流库项目,我们要做的第一布就是建立自定义行为。
      打开wwca.exe程序,选择在前面创建的dll,也就是CommunicationManager。选择输出工作路的路径,选择option中的include sender ,填写   在你的项目中使用的命名空间。
      然后选择【action】-》【Execute wca】,如果操作正确的话,在下面的结果中就会看见内容,wca.exe发送的控制信息。