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

Windows Workflow (WF) 作为 WCF 服务

starIconstarIconstarIconstarIconstarIcon

5.00/5 (11投票s)

2009年7月16日

CPOL

11分钟阅读

viewsIcon

117038

downloadIcon

3227

顺序工作流作为 WCF 服务。创建工作流自定义活动,从父级调用子工作流。使用配置文件配置工作流运行时服务。规则的基本思想,使用规则编辑器创建规则。在运行时应用规则。

引言

在 Windows Communication Foundation (WCF) 上处理 Windows Workflow Foundation (WF) 时,我找不到任何“实用”的文档/示例来一步一步地解释需要做什么以及“为什么”。本文档介绍如何将顺序 WF 创建为 WCF 服务。

那么,我们在这里将学到什么?

  1. 在 Windows Communication Foundation 上创建顺序工作流服务库。
  2. 使用“ReceiveActivity” - 创建契约、绑定属性等。
  3. 创建一个网站将库公开给外界(Web 配置等)。
  4. 创建一个自定义工作流运行时服务并在服务模型部分进行配置。
  5. 使用 DependencyProperty 创建带有自定义事件和属性的自定义活动。
  6. 创建自定义活动以从另一个工作流调用工作流。另外,如何传递参数。
  7. 规则的基本思想,如何使用规则编辑器创建规则集。在运行时应用规则。

背景

正如我所提到的,WCF、WF、规则是非常有趣的技术,但同时,从 MSDN 和其他网站收集所有必需的基础信息也是一项耗时的工作。我只是想涵盖该领域内所有必要的功能。

使用代码

一个简单的业务场景会让这一切更有趣。所以,让我们来定义它

  1. 预订单详情(或原始订单详情)来自外部源。
  2. 客户端调用“订单处理器”来处理数据。
  3. “订单处理器”对原始数据应用业务规则,并决定接受哪些订单,拒绝哪些订单。
  4. 它还对每个流程(订单)应用特定的业务规则(现有客户的运费折扣),并将订单和运费详情保存到数据库。
  5. 业务用户可以随时更改规则。
  6. 可选地,调用者还可以定义输入文件是否需要存档。[仅仅是为了让我们的主工作流更有趣。]

Business Scenario

从技术角度来说

  1. 订单处理器是我们在 WCF 上的主工作流。工作流将保持活动状态(活着),直到我们显式关闭它,以便它可以按需处理我们的请求。
  2. 主工作流对文件级别(所有记录)应用规则。在此示例中,如果订单金额为零,则拒绝订单(数据质量规则)。
  3. 在处理文件级别规则后,它将调用另一个子工作流(将是自定义活动)来处理工作流,并传递完整的“订单”对象。[在这里我们将看到如何使用“DependencyProperty”定义属性以及如何使用它。]
  4. “Worker”工作流对订单级别(ShipmentCostRule)应用规则,并计算运费。最后,它使用 LinQ 将订单保存到数据库。
  5. 这两个工作流都将使用自定义工作流运行时服务 ExternalRuleSetService,该服务将用于从数据库检索规则。
  6. 我们还将创建两个自定义活动,“InvokeWorkflow” - 用于调用另一个工作流,以及“PolicyFromService” - 用于应用规则,它内部将使用工作流运行时服务从数据库拉取规则。这还将有一个自定义活动事件,该事件将在应用策略后触发。
  7. 我们将使用规则编辑器来创建规则。

参考文献

我从 Microsoft samples 中借用了一些示例,其中包含规则编辑器、PolicyFromService、示例用法等。

入门

好了!让我们先确定我们的文件夹结构。

Business Scenario

  • Lib - 将包含各种应用程序在运行时使用的所有二进制文件。
  • Src/Libraries - 将包含所有库(自定义活动、自定义工作流运行时服务等)。
  • Src/RuleEditor - 来自 Microsoft 示例的规则编辑器,我没有对此做任何特殊处理。
  • Src/Services - 我们在 WCF 服务上的主工作流,以及用于托管该服务的一个网站。
  • Src/TestClient - 用于调用服务的测试客户端。

创建主工作流

  1. 创建新项目 - > WCF - > 顺序工作流服务库。
  2. new_wcf_sequential_wf_service_library.JPG

  3. 将创建一个顺序工作流,其中包含一个“ReceiveActivity”和一个接口“IWorkflow1”。将工作流“Workflow1”重命名为“ProcessManager”,将接口“IWorkflow1”重命名为“IProcessManagerServices”,因为这将是我们向外部公开的服务。
  4. 打开 IProcessManagerServices。将现有操作契约重命名为 string Start(bool fileToBeArchived)
  5. 打开“ProcessManager”,您会注意到 receiveActivity 上有一个红色的感叹号。别担心,这是因为我们重命名了契约“getData”。
  6. wf_initial

  7. 更正契约操作名称,属性 --> ServiceOperationInfo --> 选择“Start”操作。
  8. 我们需要绑定输入参数“fileToBeArchived”。
    1. ProcessManager --> 查看代码。
    2. 定义一个变量 public bool FileToBeArchived;
    3. 回到设计模式。
    4. 点击“Promote Bindable Properties”。
    5. RecieveActivity_prop

    6. 您应该会看到“FaultMessage”、“fileToBeArchived”和“OperationValidation”属性已填充。
    7. 将“FaultMessage”和“OperationValidation”属性删除并留空。我们将在下一节中学习它们。转到代码视图。删除 VS IDE 添加到您代码中的所有相关代码。您应该会看到类似这样的内容。
    8. recieveActivity_notrequired_code

    9. 点击“fileToBeArchived”并与我们定义的变量“FileToBeArchived”绑定。
  9. 我们希望我们的服务(主工作流)保持活动状态(始终在线)。为此,我们将使用“While”活动。在放置它之前,让我们先创建一个另一个变量 public bool SwitchedOn
  10. 在 receive activity 之上创建一个“Code”活动,并将其命名为“SwitchOn”。在它下面放置一个“While”活动,然后将 Start “Receive”活动拖到“While”活动内。将其命名为“WhileSwitchedOn”。
  11. 在 execute code 方法中,编写 this.SwitchedOn = true;
  12. 让我们放置另一个“Receive”活动,并将其命名为“SwitchOff”。创建另一个操作契约“SwitchOff”并相应地绑定它。
  13. 所以,我们的工作流将一直保持活动状态,直到我们不显式关闭它。
  14. 客户端将调用工作流“Start”操作,并带有一个参数,指示输入文件是否需要存档,只是为了向您展示一些“ifelse”活动在主工作流级别执行一些任务。

创建库

好了……现在,让我们在这里休息一下,创建一些其他有趣的东西!让我们创建工作流库以及所需的自定义活动“InvokeWorkflow”和“PolicyFromService”。

自定义活动需要继承自 System.Workflow.ComponentModel.Activity,并重写其 Execute 方法,仅此而已!如此简单!

  1. PolicyFromService – 我只是使用了 Microsoft 的示例,没有对其做任何特殊处理,只是添加了一个自定义事件,该事件将在应用策略(规则集)后触发。
  2. InvokeWorkflow – 重写 Execute 方法时,您只需要做以下事情:
    1. 获取服务,例如 IStartWorkflow IStartWorkflow startWorkflow = executionContext.GetService(typeof(IStartWorkflow)) as IStartWorkflow;
    2. 启动工作流 this.InstanceId = startWorkflow.StartWorkflow(this.TargetWorkflow, this.Parameters)TargetflowParameters 将由“调用工作流”或“父工作流”填充。

    使用 DependencyProperty 的自定义属性

  3. 定义一个静态 DepedencyPropertypublic static readonly DependencyProperty InstanceIdProperty = DependencyProperty.Register("InstanceId", typeof(Guid), typeof(InvokeWorkflow));
  4. 创建一个公共属性来“get”和“set”(base)值
  5. [Category("Activity")]
    [Description("InstanceId of of the invoked workflow")]
    public Guid InstanceId
    {
       get
        {
            return (Guid)base.GetValue(InstanceIdProperty);
        }
        set
        {
            base.SetValue(InstanceIdProperty, value);
        }
    }
  6. 定义一个静态 DependencyProperty
  7. public static DependencyProperty AfterPolicyAppliedEvent = 
      DependencyProperty.Register("AfterPolicyApplied", 
      typeof(EventHandler), typeof(PolicyFromService));
  8. 创建一个公共事件来“add”和“remove”事件处理程序
  9. public event EventHandler AfterPolicyApplied
    {
       add { base.AddHandler(AfterPolicyAppliedEvent, value); }
       remove { base.RemoveHandler(AfterPolicyAppliedEvent, value); }
    }
  10. 在需要的地方引发事件。
  11. base.RaiseEvent(PolicyFromService.AfterPolicyAppliedEvent,this, EventArgs.Empty);

自定义工作流运行时服务

ExternalRuleSetService – 我没有对其做任何特殊处理,只是使用了 Microsoft 的示例。大家试着理解代码的作用。我们将配置此工作流运行时服务,以便在运行时(通过“PolicyFromService”活动)可以使用它。

最后,编译解决方案“Library.sln”并将**二进制文件复制**到root\Lib文件夹,这将由我们的主解决方案引用。

回到主工作流

要使用我们的自定义库,请转到工具箱 --> 右键单击 --> 选择项。从Lib文件夹中选择我们的库。您应该会在工具栏上看到我们的组件,如下所示。

toolbar

对于我们的业务场景,在处理完文件上的初始任务(存档等)后,我们需要对已完成的输入记录应用业务规则,并相应地接受和拒绝记录。

放置“PolicyFromService”活动,命名为“DataQuality”,并设置 RuleSetName 为“DataQuality”。提醒我使用规则编辑器创建此规则**:)**

子工作流

好的!我们已经过滤了记录(预订单详情),我们需要通过应用业务规则来处理每个订单并将它们保存到数据库。为此,我们将创建另一个顺序工作流“Worker”,然后放置“Code”活动来初始化 Worker,然后应用规则,“PolicyFromService”活动,最后放置“Code”活动将处理后的订单保存到数据库。相应地命名它们,并将“PolicyFromService”活动的 RuleSetName 设置为“ShipmentCostRule”…提醒我创建此规则**:)**

Worker

我们将从主“ProcessManager”工作流调用此子“Worker”工作流。这将一直持续到所有订单都处理完毕。所以,让我们放置一个“While”活动,将 Condition 选择为“Declarative Rule Condition”,并将表达式设置为 this.OrderQueue.Count != 0。将“InvokeWorflow”活动放置在“While”活动内部。将 TargetWorkflow 设置为“Worker”(记住我们现在正在使用 DependencyProperty),通过浏览类型。

最后…我们最终的主工作流看起来像…

MainWorkflow

我们需要将必需的参数从主工作流传递到子工作流。在这种情况下,我们将传递一个完整的“订单”对象给“Worker”。我们通过“worker”工作流的“BeforeInvoke”事件来完成。

从队列中出列订单并将其作为参数传递。

OrderDetail thisOrder = this.OrderQueue.Dequeue();
Type targetWorkflow = typeof(Worker);
(sender as InvokeWorkflow).Parameters.Add("Order", thisOrder);

[注意 - 我使用了 .NET 的 Queue 来对所有记录进行排队,并逐个出列进行处理。]

创建规则

好的… :) 我们的应用程序差不多完成了,只需要使用规则编辑器创建两个规则集。希望你一直跟着我。

  1. DataQuality – 忽略所有金额为 0 的订单。我创建了一个扩展方法 FilterZeroAmountOrders 来过滤所有此类记录,我们将在本规则中使用它。
  2. Condition – this.RawOrders.FilterZeroAmountOrders().Count > 0

    Then Action – this.Orders = this.RawOrders.FilterZeroAmountOrders()

  3. ShipmentCostRule – 如果会员已购买超过 1000 美元的商品,我们将给予他 50% 的折扣。另一个扩展方法 TotalAmountOrderedTillDate 将帮助我们。
  4. Condition – this.Order.TotalAmountOrderedTillDate() > 1000

    Then Action – this.Order.ShippingCost = this.Order.ShippingCost * 0.5

我们不关心 else 部分,可以将其留空。**保存规则**。

托管

让我们通过向解决方案中添加一个空的网站来托管我们的服务。您可以删除所有内容,除了web.config。我也建议删除web.config的所有内容,以便我们知道运行此解决方案确切需要什么。这很重要…让我们将任务分为两部分。

  1. 在 WCF 服务上配置或公开我们的服务契约。
    1. 为此,我们将向网站添加一个新项“WCF Service”并将其命名为ProcessManagerService.svc。删除所有其他项和 VS IDE 创建的任何文件夹。
    2. website solution

    3. ProcessManagerService.svc中,我们需要说明我们要公开哪个服务。我们的服务实际上是一个名为“ProcessManager”的工作流。连同其 Factory 一起提及,该 Factory 负责将工作流作为服务公开的内部“事物”。
    4. Factory="System.ServiceModel.Activation.WorkflowServiceHostFactory" 
      Service="BigDs.WF.Services.ProcessManager"
    5. 根据旧的 WCF “ABC”规则定义其终结点。有关详细信息,请查看web.config
  2. 在服务模型下配置我们的自定义 WorkflowRuntime 服务,因为 WCF 是我们工作流的主机。此配置节位于 <system.serviceModel> --> <serviceBehaviors> --> <behavior> --> <workflowRuntime>
    1. 按如下方式添加工作流运行时服务。
    2. <workflowRuntime>
       <services>
        <add type="BigDs.WF.WorkFlow.ExternalRuleSetService, BigDs.WF.WorkFlow" />
       <services>
       <commonParameters>
         <add name="ConnectionStringName" value="cs" />
       </commonParameters>
      </workflowRuntime>

将网站设置为启动项目并运行它。点击ProcessManagerService.svc以验证我们的服务是否已正确配置。

我们差不多准备好了,这次我是认真的 **:)**,只需要创建一个客户端来调用。

客户端

创建一个简单的控制台应用程序,添加一个服务引用,并按如下方式调用 WCF 方法……

ProcessManagerServicesClient psc = new ProcessManagerServicesClient();
psc.Start(true);

仅此而已,各位……运行您的网站,执行客户端来测试应用程序。请告诉我您对本文的看法,或者如何改进/增强它。

让我们快速总结一下今天学到的所有内容。

  1. WCF 顺序工作流 (WF) 服务库。
  2. 将其公开为 WCF 服务,并将其托管在网站上。
  3. 将其与工作流运行时服务一起配置。
  4. 创建自定义工作流运行时服务。
  5. 创建自定义活动。
  6. 使用 DependencyProperty 创建自定义属性。
  7. 使用 DependencyProperty 创建自定义事件。
  8. 规则集的基本概念。
  9. 如何使用规则编辑器创建规则集。
  10. 从数据库获取规则并通过工作流运行时服务在运行时应用它。
  11. 创建自定义活动以调用子工作流。
  12. 如何将参数传递给子工作流。
  13. 如何使用扩展方法并在规则编辑器中创建规则。

关注点

我们可以在 <workflowRuntime> 下定义 <commonParameters>。我仍在尝试找到一种方法来从配置中读取它。唉!搜索这些小东西真的很烦人。也许以后再说。

运行代码

  1. 解压文件。
  2. 执行Script文件夹下的脚本。这将创建两个表,其中包含一些现有数据(两条规则和三个现有订单详情)。
  3. 更新 RuleSet 表的 AssemblyPath 以匹配您的路径。这很重要。
  4. web.config中更新 InputFileLocation 为您的路径。

历史

  • 1st 版本,于 2009 年 7 月 16 日发布。
© . All rights reserved.