65.9K
CodeProject 正在变化。 阅读更多。
Home

主机和工作流:两个沟通的世界 - 第二部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (15投票s)

2008年10月3日

CPOL

8分钟阅读

viewsIcon

54792

downloadIcon

411

第二部分:互通:通过 CallExternalMethod 活动实现工作流 -> 宿主通信。

引言

这是关于宿主工作流通信的五篇文章中的第二篇。

在本系列文章中,我将尝试展示实现通信的各种可能性,从最简单的情况到更复杂的情况。我并不打算详尽无遗,但我将尝试提供一个总体的概览,以便我们对该主题有一个很好的认识。

因为我也不喜欢冗长的文章,所以我将本系列分为以下几部分

背景

在第一部分中,我们了解了如何在启动工作流时向其传递信息。当您不需要从工作流返回数据时,这种方式可以正常工作。但通常,您需要返回有关工作流中已处理数据的信息。

工作流基础结构提供了一个活动组件来完成这项工作:CallExternalMethod 活动。

回到第一部分的示例,我们的程序从计算机读取一个文件,并从中读取一定数量的字符。在此程序中,我们希望传递两个参数(文件路径和要从文件中返回的字符数),工作流将在内部消息框中显示信息。现在,我们有了将返回信息直接显示在控制台输出而不是内部消息框中的需求(参见图 1)。

您可以看到,在工作流调用过程中,线程之间的障碍被跨越了,然后再次将字符从文件返回到控制台宿主应用程序。

为了实现将信息从工作流传递到宿主,WWF 提供了一个名为 CallExternalMethod 的专用活动。这是好消息;坏消息是,我们必须创建一个特定的服务模式来与此活动通信。

通信服务模式作为另一个服务工作。您必须创建一个服务接口,实现该服务,并将其注册到工作流运行时实例中。您可以按自己的意愿实现通信,通过轮询或事件,但您必须满足 CallExternalMethod 的要求。简而言之,您需要实现以下代码结构

创建以下接口和类

  1. 一个包含由 CallExternalMethod 活动调用的方法定义的接口。
  2. 一个实现该接口的类。在此类中,您实现由 CallExternalMethod 调用的方法。在此方法中,您编写代码将信息发送到宿主。
  3. 如果您想使用事件来驱动通信,您应该实现一个特定的 EventArgs 派生类来保存您想要发送到宿主的信息。

在宿主应用程序中,需要执行以下步骤

  1. 您必须创建一个 ExternalDataExchangeService 类的实例并将其注册到工作流运行时。
  2. 您必须在此数据服务类中注册您创建的通信服务。
  3. 如果您使用事件来驱动通信,您还必须在宿主类中处理通信事件。

在工作流中

  1. 您必须有一个 CallExternalMethod 活动。
  2. 在其中,注册通信接口和要调用的方法。

您可以在下面的图表中看到我们示例的结果类结构

您可以看到 CallExternalMethod 调用了另一个线程中的方法。当该方法执行时,我们就在宿主线程中获得了结果!这就是技巧。

我们使用控制台应用程序和顺序工作流来详细解释如何创建这些结构。

代码实践

最好的做法是下载附带本文的源代码,或者如果您已经拥有第一部分的源代码,则可以重复使用。

配套项目通过参数实现了与工作流的通信,这在第一部分中已得到解释。我假设您会知道如何做到这一点(如果您不知道,请回顾本文系列的第一部分)。

现在我们将逐步解释在我们的示例中如何使用 CallExternalMethod

A. 创建以下接口和类

  1. 一个包含由 CallExternalMethod 活动调用的方法定义的接口。
  2. 您可以在与宿主相同的项目中创建此接口,也可以在独立的项目中创建。为简单起见,我们在同一个宿主应用程序中创建了它。该接口仅包含要由工作流调用的方法。您现在应该在想,为什么不能直接调用宿主中的任何方法?答案是,此接口必须有一个特殊的头部来将其定义为工作流可调用的外部方法。完整的代码显示在此处

    ///<summary>
    /// You MUST to mark the interface with the header 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); 
    }

    请注意 ExternalDataExchangeHeader。它确定类中的方法可以从工作流调用。

  3. 一个实现该接口的类。
  4. 此类的代码由您负责和决定。您可以决定实现一个方法来简单地保存从工作流传递的数据,然后宿主将轮询该类的实例,或者您可以决定是否实现一个事件以异步获取数据。

    在我们的代码中,我们实现了一个事件。我们应该将数据从工作流传递到事件的参数中

    /// <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);
            }
        }
    }

    您可以看到上面事件的实现和方法的简单代码。该方法仅将响应输入参数传递给事件参数并引发事件。

    事件参数的定义很简单,并在此处显示

    public 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; }
       }
    }

在我们的示例中,我们在不同的文件中创建了三个类。您可以在此处看到它们

  • SendDataToHostEventArgs.cs
  • CommunicationService.cs
  • ICommunicationService.cs

B. 将通信服务注册为 ExternalDataExchangeService

我们在宿主程序的工作流运行时实例中注册它。工作流-宿主互通类必须注册为运行时中的外部数据交换服务。注册相对简单,如您在以下代码中所示

//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.................

前四条指令与注册互通服务相关,最后一条与注册用于通信工作流结果的事件相关。我们的应用程序在宿主方面已完成。现在我们应该构建我们的工作流。

C. 在工作流中…

嗯,我们以第一部分中的相同工作流为基础。我们删除显示 MessageBox 的代码活动。然后,将 CallExternalMethod 活动拖放到工作流中,并按以下图形配置它

请注意,InterfaceType 属性已填充为创建的 ICommunicationServiceMethodName 是活动要在接口中调用的方法。当您输入 MethodName 时,属性会展开,您必须声明将填充响应参数的变量。在我们的应用程序中存在一个变量 _response。为了使参数与变量匹配,请单击 Response 字段中的省略号按钮,并在打开的对话框中,单击“绑定到新成员”,创建字段,然后输入字段名 _response。(如果您单击示例代码,绑定已创建,您可以在“绑定到现有成员”屏幕中直接看到变量)。

其余逻辑很简单,您可以在示例中看到它(访问文件并从宿主传递初始化参数的逻辑)。

编译应用程序并运行它。您可以使用自己的参数来启动应用程序,或使用默认参数。(您可能没有使用默认参数的特定文件。可以随意将其更改为您拥有的其他文件)。

总结

使用 CallExternalMethod 活动将信息从工作流传递到宿主的基本步骤如下

创建以下接口和类

  • 一个包含由 CallExternalMethod 活动调用的方法定义并带有头部的接口
  • [ExternalDataExchange]
  • 一个实现该接口的类。在此类中,您实现由 CallExternalMethod 调用的方法。

在宿主应用程序中

  • ExternalDataExchangeService 类的实例注册到工作流运行时。
  • 将通信类的实例添加到 ExternalDataExchangeServiceInstance
  • 如果您使用事件来驱动通信,您还必须在宿主类中处理通信事件。

在工作流中

  • CallExternalMethod 活动拖放到工作流中您希望将信息发送到宿主的点。
  • CallExternalMethod 活动的属性中,注册通信接口、要调用的方法以及用于匹配调用方法参数的变量或属性。

现在,您知道了如何实现宿主与工作流之间的双向通信。但您仍然会遇到限制。到目前为止,您只知道如何在初始化阶段向工作流发送信息,但如何随时从宿主向工作流发送信息呢?我们将在下一部分中讨论这个问题。

历史

  • 初版 - 2008.10.03。
© . All rights reserved.