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

宿主与工作流:两种沟通世界的交互方式。第五部分

starIconstarIconstarIconstarIconstarIcon

5.00/5 (4投票s)

2008年10月4日

CPOL

7分钟阅读

viewsIcon

39215

downloadIcon

439

第五部分:使用相关性参数与工作流实例进行内部通信。

引言

这是关于宿主-工作流通信的五篇文章中的最后一篇。

本系列文章旨在展示工作流与宿主应用程序之间实现通信的各种可能性,从最简单的场景到更复杂的场景。我无意穷尽所有细节,但会尽量提供一个总体概览,以便我们能对该主题有一个清晰的认识。

由于我也不喜欢冗长的文章,因此我将它们分成了以下几个部分:

第五部分:使用关联参数与工作流实例进行互通信。

背景

到目前为止,我们已经研究了事件在同一工作流实例中仅被调用一次的通信示例。在本篇文章中,我们想了解的最后一种情况是,您在工作流的不同部分使用相同的外部事件。

工作流运行时可以找到特定工作流实例,但在您尝试绑定事件时,它会发现工作流中存在两个或更多相同的事件实例,而只有一个需要接收数据(当然,对我们而言)。如何避免工作流混淆我们的数据?通常,运行时会从左到右处理信息,也就是说,位于右侧分支的活动会首先执行,但这并不能保证总是能成功。

解决方案是关联参数。

关联挖掘是指您“关联”工作流中的两个或多个活动。当您调用外部活动以从宿主请求信息时,过程开始。此请求应初始化关联参数。当您将信息返回工作流时,会使用相同的关联参数值将其发送出去。在工作流中,接收到的信息会发送到“标记”有与调用外部活动相同的关联令牌的“处理外部事件”活动。

这可能会令人困惑。用于宿主关联方法与事件的关联参数与用于工作流关联两个(或更多)活动但协同工作的关联令牌不同。

关联参数使用值来标识宿主中关联的操作,而关联令牌在工作流中工作,并且仅使用其名称,值无关紧要。

我们可以通过以下应用程序来说明这个概念。

代码实践


伴随代码使用

使用 ExampleWithOutCorrelation 解决方案来查看没有关联的代码。您也可以在其中添加本文所述的必要修改,将工作流转换为带关联的工作流。如果您想了解带关联的工作流应该是什么样子,请使用 ExampleWithCorrelation 解决方案。


假设您有一个工作流,它接收客户姓名并检查他/她是否有订单。如果他/她有订单,工作流将并行地向宿主询问订单提供商的电子邮件地址和客户的电子邮件地址。当工作流获取电子邮件地址后,它会为每个地址发送一封通知电子邮件,并告知宿主电子邮件已发送。

下图说明了工作流。

通常情况下,这可以正常工作,并且运行时会在并行活动中的每个分支中分配正确的值,但并非总是如此。如果宿主应用程序由于某种原因延迟了第一个分支的响应信息,而第二个分支的响应先到达,那么程序就无法正常工作。我们在示例中通过引入延迟来强制出现这种情况。在这种情况下,响应结果如图所示。

您可以看到,宿主发送的第一个响应被工作流接收并用于工作流的两个分支。

显然,运行时无法知道 getEmailAddress 请求了什么信息,更重要的是,它也不知道 sendEmailToWorkflow HandlerExternalEvent 接收器获取的返回的电子邮件地址是什么。您可以在附件中查看此程序。

通常,工作流从左到右处理信息,但您不能保证响应也会从左到右发送,而这正是本示例所暴露的情况。这种行为的一个常见原因是,在将信息返回给工作流之前访问数据库或 Web 服务。

在这种情况下,我们能做什么?“关联”:我们需要告知运行时,活动 getEmailAddress1 与活动 sendEmailToWorkflow1“相关联”,而活动 getEmailAddress2 与活动 sendEmailToWorkflow2“相关联”。

其次,我们不必关联工作流中的所有外部操作。如果您创建一个使用关联的接口,那么接口中的所有内容都将被关联。当您不提供关联参数时,NET 框架在程序运行时会抛出错误。

解决方案:创建一个第二个通信接口,并将两者都用于创建您的 Communication 管理器类。在第二个接口中,放置您不希望关联的外部方法和事件。在我们的示例中,唯一外部方法是 SendMailNotification

在继续之前,请打开 ExampleWithoutCorrelation 解决方案,生成它并观察其工作方式。我们将以此应用程序为基础来创建我们的带关联应用程序。在附加代码中:打开 ExampleWithoutCorrelation 解决方案,打开 CommunicationManager 项目,然后打开 IMailConfiguration 文件,并按如下方式修改该文件:

[ExternalDataExchange]
[CorrelationParameter("typo")]
public interface IMailCommunication
{
  #region Workflow To Host
  /// <summary>
  /// Get the email address based on the typo information.
  /// </summary>
  /// <param name="typo">PROVIDER,  CUSTOMER</param>
  /// <param name="name">Name of the </param>

  [CorrelationInitializer]
  void GetEmailAddress(Guid wfId, string typo, string orderNumber);

  #endregion

  #region Host To Workflow

  /// <summary>
  /// Send the email to workflow
  /// </summary>
  [CorrelationAlias("typo", "e.Typo")]
  event EventHandler<MailEventArgs> SendEmailToWorkflow;

  #endregion
}

请注意,您必须删除 SendEmailNotification 方法;此外部方法必须转换为一个附加接口。在 IMailCommunication 中,您只需要添加 [CorrelationParameter] 标头,它告诉运行时您将此类型声明为关联参数。然后在外部方法中,[CorrelationInitializer] 标头在调用方法时启动关联过程,然后接口中的所有方法或事件都处于此关联过程之下。所有不具有关联参数的方法或事件在此上下文中调用时都会引发异常。

由于我们不需要 SendEmailNotification 具有关联参数,因此我们可以将其声明在另一个通信接口中。

在打开的项目中创建一个新的接口文件,并将其命名为 IMailConfiguration2,然后输入以下代码:

[ExternalDataExchange]
public interface IMailCommunication2
{
  /// <summary>
  /// Notify Host that a email was send
  /// </summary>
  /// <param name="wfId"></param>
  /// <param name="Notification"></param>
  void SendEmailNotification(Guid wfId, string Notification);
}

您可以看到它是一个普通的 ExternalDataExchange 接口。

现在,我们需要修改 IMailCommunication 实现以兼容这两个接口:您需要在类声明中添加第二个接口的名称。请参阅代码:

public class MailCommunication:IMailCommunication, IMailCommunication2
{
  ..........
}

您无需进行任何其他修改。

我们的通信接口就到此为止。现在,我们需要使用 wca.exe(或 WWCA.EXE)生成自定义活动(请参阅本文第四部分。您可以在 此处 下载 WWCA)。调用 wwca.exe,将 CommunicationManager.dll 作为输入,并将输出发送到 CorrelationDemoWF 目录。

生成活动并打开 CorrelationDemoWF 项目,您现在可以看到来自 IMailCommunication 的生成活动带有一个红点。请参阅图示:

来自 IMailCommunication2 的生成活动没有标记为红色。这是因为我们仅在第一个接口中应用了关联参数。

红点表示您必须为这些活动输入关联令牌,这是应用关联到通信接口后出现的一个新属性。

关联令牌是任意的,您只需为相关联的活动选择相同的令牌即可。在我们的情况下,我们为 getEmailAddress1sendEmailToWorkflow1 选择 a1。当您选择一个令牌时,会创建一个新的依赖属性,您应该输入该令牌的作用域,在我们的示例中是 seqProviderEmail(容器活动)。

为左侧分支选择关联令牌 b2,并将 seqCustomerEmail 指定为两个活动的拥有者。

您可以看到活动 SendEmailNotification12 没有关联令牌。这是因为它们是在第二个接口中声明的,没有关联标头。

就这样,编译应用程序并执行。现在您可以看到结果如图所示:

关联确保数据在所有程序条件下都能正确地从源头到达正确的接收方。

关联可帮助您避免工作流应用程序中出现那些奇怪且不稳定的错误,这些错误会在繁忙服务器的延迟因素发挥作用时出现。

好了,本系列到此结束。我认为我们现在对如何获取和发送数据到工作流实例或多个实例有了更好的了解。我们还展示了如何处理创建自定义实体以及创建通信接口的方法。

历史

  • 初版:2008.10.04
© . All rights reserved.