工作流服务

使用 WCF 和 WF 4 的工作流可视化设计

Leon Welicki

下载代码示例

开发人员越来越多地采用面向服务的体系结构 (SOA) 作为构建分布式应用程序的方式。对于缺乏经验的人,设计和实现面向服务的分布式应用程序可能会令人生畏。然而,Microsoft .NET Framework 4 使得使用 Windows Workflow Foundation (WF) 实现 Windows Communication Foundation (WCF) 服务变得比以往任何时候都更简单。

WCF 工作流服务为编写长时间运行、持久的操作或服务(其中通过排列操作的顺序强制实施应用程序协议十分重要)提供了一个高效的环境。工作流服务是通过使用可利用 WCF 发送和接收数据的 WF 活动来实现的。

在本文中,我将说明如何组合 .NET Framework 4 中引入的 WCF 和 WF 的许多功能,以在不编写代码的情况下,建模一个用于房地产公司的长时间运行、持久且经过检测的抵押审批流程。本文的意图既非概括介绍 WCF 或 WF,也非逐步向您介绍创建工作解决方案的整个流程。我打算通过实际业务应用场景重点介绍新的重要 .NET Framework 4 功能的使用。本文的代码下载中包含一个完整的工作解决方案。

应用场景

首先,让我们概要介绍一下围绕其构建工作流应用程序的应用场景。Contoso Housing 是一家销售普通住宅和高级公寓的房地产公司。为提供更优质的客户服务和端到端购买体验,Contoso 与帮助潜在客户解决其抵押需要的三家抵押公司合作。每家抵押公司提供不同的利率。Contoso 按照抵押供应商的利率对其进行优先级排序,以确保客户获得最佳交易(采用更好的利率使房屋销售出去的可能性更大这一假设)。

客户通过 Web 应用程序提供其抵押请求数据。每个客户输入一个客户 ID、房屋价格、首付金额、贷款期限(年)、工资信息以及某些背景核实信息。

申请工作流的第一步(参见图 1)使用客户输入的数据筛选客户并确定是否具备资格,然后将请求发送至抵押供应商。


图 1 抵押审批流程

申请遵循以下规则:

  • 如果申请人被取消赎回权、破产或正处于诉讼中,申请将被拒绝。
  • 如果申请人没有信贷记录且提供低于 20% 的首付款,则会将申请返回进行修改(申请不恰当),但不是被拒绝。客户必须提供至少 20% 的首付款才能继续。
  • 如果上述情况均不适用,将批准申请。

Contoso 根据其提出抵押请求的首选顺序与抵押供应商联系。如果抵押供应商拒绝申请人,则询问下一个。所有供应商均实施标准的抵押审批请求服务约定。在审批流程的此阶段期间,客户应该能够查询其请求的状态。

在抵押申请解决后(一个供应商接受或全部拒绝),通过 CRM 系统提供的一项服务将客户交互记录到 Contoso 的客户关系管理 (CRM) 系统中。然后,将结果返回给客户。

请注意,图 1 中显示的抵押审批流程概括介绍了业务流程,但并未说明如何实施。工作流服务将是对此流程的实施。

创建声明性服务

工作流服务将从客户那里接收数据,进行初步筛选,协调与抵押供应商的沟通,将交互登记到 CRM 服务中并将结果提供给客户。

该服务将是长时间运行的(可能需要几天或几个月才能完成)、持久的(可以先保存状态,稍后重新开始)且经过检测的(开发人员和用户均能够在无需调试服务的情况下了解正在执行的作业)。通过使用 WCF 和 WF,您可以在不编写任何代码的情况下以声明方式全部实现这些目标,您只需组装和配置 .NET Framework 提供的组件。图 2 显示了解决方案的体系结构图。


图 2 解决方案的体系结构

WCF 工作流服务包含在定义工作流的 .xamlx 文件中。您可以使用 WF 设计器以可视化方式定义业务流程,服务约定可基于工作流的结构进行推断。

要在 Visual Studio 2010 中创建工作流服务,请创建一个新项目,然后选择“WCF 工作流服务应用程序”模板。此项目模板会使用 Receive 和 SendReply 活动创建一个十分简单(但运行)的工作流。这类似于一个具有接收整数并返回一个字符串的方法的类。您可以通过向其中添加更多活动来扩展工作流。这些活动可能会添加执行逻辑或服务操作。

要定义服务约定,可使用 WF 提供的消息传送活动。服务的配置存储在 web.config 文件中,就像在常规 WCF 服务中一样。如果打开 web.config,您将会看到一个非常干净的配置文件,因为 WCF 工作流服务利用了 WCF 4 中引入的服务配置增强功能。

消息传送活动以无缝方式将 WF 和 WCF 结合起来。它们设计用于支持面向消息的工作流并且更好地将消息传送集成到工作流中。借助消息传送活动,工作流能够将数据发送到外部其他系统(Send、SendReply 和 SendAndReceiveReply),以及从其他系统接收数据(Receive、ReceiveReply 和 ReceiveAndSendReply)。它们还包括用于处理关联(InitializeCorrelation、CorrelationScope)和事务 (TransactedReceiveScope) 的活动。

Receive 和 Send 活动允许在工作流内为消息传送交互建模。服务中的约定可通过在其中配置 Receive 和 Send 活动来定义。每个 Receive 活动以操作形式公开。每个 Send 活动会将消息发送到服务。目标服务无需利用 WCF,甚至是 .NET Framework,因为您是通过标准协议与其进行交互。

Receive 和 Send 活动可配置为使用基于消息或基于参数 (RPC) 的方法接收和发送实际数据。这会控制发送或接收数据的传输格式。

采用流程图描述流程

您可以使用 Sequence 建模流程,但是快速浏览图 1 表明,有时流程需要返回上一步,而顺序工作流中并不直接支持这一操作。(需要手动使用诸如具有认真建模条件的 While 活动的构造建模循环。)Flowchart 更加适合于对此应用场景进行建模。

Flowchart 是在 WF 4 中引入的新控制流活动,使您能够像在白板上一样描述您的流程。Flowchart 描述的流程实际上是按顺序组织的,具有单一执行路径,在某些情况下可能需要返回上一步。为了描述流程,Flowchart 使用箭头和方框(许多领域中的常用方法)。

“WCF 工作流服务”项目模板创建的默认工作流是一个 Sequence,但这并不意味着您只能在工作流服务中使用控制流活动。要在服务中使用 Flowchart(或任何其他复合活动)作为根活动,只需删除 Sequence,然后添加一个 Flowchart。

图 3 可以看出,轻轻松松即可从图表(企业用户通常草拟该图表以描述流程)创建一个 Flowchart。


图 3 作为一个 Flowchart 的抵押审批流程

Flowchart 是一个描述业务流程的强大工具,最大程度降低了可执行流程与其指定方式之间的不匹配性。在这种情况下,Flowchart 定义 流程的文档。

让我们将速度减慢一些。我们已经立即从空 Flowchart 跳至一个完整的 Flowchart。我们返回到空 Flowchart,开始研究它。

潜在购房者通过 Web 应用程序引入其数据。此数据通过 Receive 活动被传递给工作流。在 Flowchart 中包括 Receive 活动的结果是 WorkflowServiceHost 会公开一个端点,其允许用户通过向工作流发送消息与工作流进行通信。

正如我前面所述,您可以将 Send 和 Receive 活动配置为使用基于消息或基于 RPC 的方法。在本例中,我将 Receive 活动配置为使用基于 RPC 的方法。这表示它会接收一组输入参数,类似于方法调用。要使这些参数可用于工作流,我需要创建变量并将它们绑定到参数(参见图 4)。另一种可行的方法是具有一个折叠所有这些字段的 DataContract。


图 4 配置输入参数

如果您希望在收到消息后创建并启动一个新的工作流实例,您需要将 Receive 活动中的 CanCreateInstance 属性设置为 true。这意味着当 WorkflowServiceHost 从此端点收到消息后将创建一个新实例。

使用 WF 组合建模筛选步骤

在工作流中拥有数据后,您便可以开始处理数据。第一步是进行筛选,表示验证一组条件以便确定申请人是否符合抵押条件,然后再与供应商联系。

一种方法是向主 Flowchart 中添加许多判断形状 (FlowDecision)。尽管该方法行之有效,但会使整个流程难以理解。此外,对筛选规则进行任何修改都需要更新主流程。Flowchart 似乎非常适合于以可视化方式表达条件,但是我们希望精简主流程。

解决方案就是在现有 Flowchart 内添加一个新的 Flowchart。WF 4 在本质上为组合提供强大支持,因此可自由地组合活动。这意味着您可以在需要的地方添加一个新的 Flowchart,包括在现有 Flowchart 内。此外,组合是随意进行的并且不会施加任何限制。您可以在方便时组合现有活动。

子流程图显示折叠在父流程图内(Flowchart 设计器不支持就地展开)。您需要双击流程图以建模筛选逻辑。筛选 Flowchart(参见图 5)是主 Flowchart 的一个子项,可访问其变量和参数。


图 5 添加筛选流程图

如果要编写一些代码,该怎么办?当然,您可以编写代码以描述筛选流程。例如,在本例中,您可以编写一个 CodeActivity,以从客户那里接收数据作为输入,执行验证(一组采用所选语言的链接 if 语句)并返回结果。这有其自身的利弊。它可能会提供更好的性能(所有验证均在一轮执行操作中执行),以及提供比声明性方法更加简明的表述。另一方面,您会失去流程的可视表示形式(不透明性), 而且更改流程需要修改代码和重新编译。

将结果发送给客户

当筛选验证完成后,我需要将结果返回给申请人。我在工作流开始时通过 Receive 活动收到申请人的数据。我使用 SendReply 活动返回答复(通过右键单击“Receive”并选择“Create SendReply”可以为现有 Receive 创建一个 SendReply)。Receive 和 SendReply 的组合允许实现请求-响应消息交换模式。SendReply 配置为将操作的结果和一条说明消息返回给客户。

为什么选择使用 SendReply 而不使用 Send?您可以使用一对 Receive 和 Send 活动建模双向消息交换模式,如“请求并等待响应”(类似于回调),但 SendReply 更适合于建模请求-响应消息交换模式(类似于方法调用)。

确定接下来要执行的操作

完成筛选后,工作流可以继续执行抵押申请流程。此时,有两个分支:Reject 和 Approved,但是当申请人需要提供更多数据时,工作流必须返回上一步。Flowchart 允许将这一画一条直线 操作建模到工作流将继续的步骤。

我使用 FlowSwitch 活动(类似于 switch 语句)确定基于筛选结果要采用的路径(如图 6 所示)。


图 6 FlowSwitch 中的每个出站箭头代表 Switch 中的一个用例

关联

当抵押请求被视为不正确时,工作流会要求客户向现有工作流实例提供其他信息。Receive 活动怎样才能知道其接收的数据是客户先前所提供数据的更正?换句话说,您怎样才能将另一条消息发送给正在运行的工作流实例?答案是关联。

WF 4 为关联引入一个框架。关联实际上是以下两种方法之一:

  • 一种将消息分组到一起的方法。一个典型的示例是 WCF 中的会话,甚至只是请求消息与其答复之间的关系。 
  • 一种将一段数据映射到工作流服务实例的方法。

.NET Framework 4 中提供了许多类型的关联,我在本示例工作流中使用基于内容的关联。基于内容的关联从传入消息获取数据,然后将其映射到现有实例。当工作流服务具有多个从单一客户端访问的方法,且交换的消息中的一段数据标识所需实例时,使用基于内容的关联。

为了设置关联,我声明了一个 CorrelationHandle 类型的变量。这是用于存储关联信息的句柄(配套解决方案中的 customerCorrelationHandle)。下一步是使用关联句柄。Receive 活动的属性网格具有一个 Correlations 部分,用于配置关联。其具有的属性可用于配置关联句柄 (CorrelatesWith),指定您通过关联查询关联的数据 (CorrelatesOn) 以及初始化关联处理程序 (CorrelationInitializers)。我在 Receive 活动中配置了 CorrelatesWith 和 CorrelatesOn 参数,以关联 customerCode 字段。

CorrelatesOn 和 CorrelatesWith 可能会造成混淆。下面的规则可帮助您进行区分:Receive CorrelatesWith 是一个现有关联句柄,CorrelatesOn 是由关联查询指定的数据。

所有这些参数均可使用 WF 设计器进行设置。

我希望关联客户标识,因此我创建了一个从消息中提取 customerCode 的关联查询。请注意,关联查询是使用 XPath 编写的,但是您无需了解 XPath,因为 WF 设计器会通过为您检查收到的消息的约定创建查询。

工作流中的第一个 Receive 活动配置为在收到消息时创建新的工作流实例。然而,当我回退到该 Receive 活动时,它不会创建新实例,因为它还配置为关联 customerCode。在创建新实例之前,它会使用该关联键查找现有实例。

向供应商询问利率

如果申请通过筛选,则会将其发送给供应商进行评估。请注意,Contoso 与三家供应商合作,并且具有与他们开展业务的首选顺序。因此,流程会按顺序询问供应商,直到有一家供应商批准。

所有供应商均实施标准的抵押审批请求服务约定。向三家抵押供应商请求利率之间的唯一区别是服务的 URI。然而,由于供应商利率请求涉及发送消息和异步等待响应,同时同步响应客户请求的状态,因此使其变得复杂。

我可以将此流程建模一次,然后在工作流中复制三次,但是这会导致许多不必要的重复,从而导致严重的可维护性问题。我希望精简此步骤,因此我可以多次使用同一流程。理想情况下,我将为工作流提供相同的输入数据,并在操作完成后获取结果,这正是自定义活动的作用。

WF 4 提供以下两种方法用来创建自定义活动:

  • 声明性通过组合其他现有活动来创建新活动。
  • 命令性通过编写代码创建新活动。可派生许多具有不同功能的基类,包括 CodeActivity(简单的命令性行为)、AsyncCodeActivity(异步执行操作)和 NativeActivity(与 WF 运行时交互)。

活动的参数在活动可接收的数据 (InArgument) 以及在它们完成后将返回的数据 (OutArgument) 方面定义其公开签名。我的活动将接收客户抵押信息和服务 URI 作为输入,将返回利率和结果字符串消息。

我在 Visual Studio 中使用“活动”项模板以利用 WF 设计器以声明方式创建新活动。设计器允许您通过拖放现有活动并设置其参数来以可视化方式编写自定义活动。使用设计器编写自定义活动的结果是一个定义 x:Class 的 XAML 文件。

我将此活动称为“AskVendor”。AskVendor 活动从客户那里接收抵押信息和服务的 URI 作为输入,并提供利率和结果字符串消息作为输出。如果利率为 0,则表示申请已被拒绝。

AskVendor 向抵押供应商发送一条消息询问利率,然后等待供应商的响应。此响应可能需要几分钟、几小时甚至几天才能到达。在等待响应的同时,申请人可能想了解流程的状态。因此,活动还可以响应来自申请人的状态请求消息。

为了同时处理这两项操作,我使用一个 Parallel 活动作为自定义活动的根。在一个分支中,我具有所有用于与抵押供应商进行通信的活动,在另一个分支中,具有用于侦听客户同时等待供应商的响应的活动。AskVendor 内使用的所有消息传送活动均配置为关联 customerCode 字段。图 7 显示了 AskVendor 自定义活动。


图 7 AskVendor 自定义活动

正如我所述,自定义活动的根为 Parallel。左侧分支向供应商发送消息并等待响应。收到响应后,它会设置结果字符串消息的格式并将完成的标记设置为 true。右侧分支侦听来自申请人的状态查询请求。Receive 和 SendReply 均处于 While 活动内,该活动在完成标记设置为 true 之后才执行。Parallel 活动的完成状态(在某个分支完成后执行)将完成标记设置为 true。因此,当左侧分支完成(收到来自供应商的消息)后,完成变量标记为 true 且右侧的 While 也完成。

自定义活动几乎与任何其他活动一样。当您创建一个自定义活动时,它会自动显示在活动工具箱中。而且使用它与使用任何其他现有活动没有任何区别:将其从工具箱拖放到设计器中,然后配置其参数。因为我没有为此活动创建设计器,因此为其分配了默认设计器(一个矩形,您可以在其中设置 DisplayName 属性)。属性窗格会自动显示活动的所有参数。

我在前面提到 WF 4 为组合提供强大支持,这同样适用于自定义活动。如果您创建自己的复合活动,您将能够自由地将其与任何其他现有活动(如,Sequence、Flowchart、Parallel),甚至是其他自定义活动组合。

调用 CRM 服务

Contoso 的 CRM 系统的核心功能为提供服务。其中一项服务允许登记与客户的交互。我可以使用 Send 活动调用它,但是这意味着手动配置活动并导入其服务数据约定。

最理想的情况是,我能够只将服务导入 WF 中,然后执行它。这正是在工作流服务项目中“添加服务引用”所完成的工作:有了服务约定,它会自动创建代理活动(为服务中每项操作创建一个),这些活动可用于调用服务(参见图 8)。在本例中,CRM 服务约定具有三项操作,因此“添加服务引用”创建了三个活动,这些活动显示在工具箱中。


图 8 向 CRM 服务添加服务引用

传达结果

最后,我需要将结果提供给客户。为简化应用程序,我只使用 ReceiveAndSendReply 活动将结果公开给客户。在客户读取结果后,工作流即完成。

为此,我需要在 Flowchart 中拖放一个 ReceiveAndSendReply。请注意,当您将活动拖放到设计器表面时,您会获得一个折叠的 Sequence。这是因为 ReceiveAndSendReply 是一个活动模板,这意味着它是一个预先配置的活动集(在本例中,是一个具有 Receive 和 SendReply 的 Sequence)。前面当我添加子 Flowchart 用于进行筛选时您已经看到。

要配置 ReceiveAndSendReply,我需要深入了解并在 Receive 活动中设置端点消息。我还需要将 Receive 配置为按客户标识进行关联,以便在客户使用其 customerCode 发送消息时,他将获得响应。

长时间运行的工作

工作流将审批请求发送给供应商后,服务实例将处于空闲状态,等待响应。此响应可能需要几分钟、几小时、几天甚至几周才能到达。这会带来许多有趣的挑战。您很可能不希望将所有实例都存储在内存中,因为这会占用不必要的系统资源而且不能缩放。而且,如果主机进程崩溃(或者在决定性不大的应用场景中,需要关闭以进行维护),任何未完成的实例都将丢失。

如果当某个实例不执行任何工作时,您可以只将其保存到持久存储中并将其从内存中删除,岂不是很好?您可以通过 WF 持久性框架,其允许将工作流实例保存到存储介质中以供稍后检索。

这意味着实例未被绑定到任何现有进程或计算机。在示例工作流中,筛选可在一个进程中执行,向第一个供应商询问利率可在另一个进程中执行,接收响应可在第三个进程中执行,而不影响工作流实例的执行或其数据。这实现了更好地利用资源,改进可伸缩性以及提供恢复能力,主机崩溃并不会导致活动实例丢失,因为可从上次保存它们的时间点恢复它们。

工作流实例被保存到实例存储中。WF 4 包括一个基于 SQL Server 的实例存储,由于持久性框架是可扩展的,因此您可以编写自己的实例存储。(例如,SDK 中的 PurchaseProcess 示例显示了如何编写一个十分简单的文本文件实例存储。)

我使用内置 SQL Server 实例存储保存 Contoso 工作流的实例。值得高兴的是,您无需编写任何代码即可使用它。在工作流服务中,持久性行为可在 web.config 文件中通过如下方式配置:

<!--Set up SQL Instance Store-->
<sqlWorkflowInstanceStore connectionString="Data Source=.\SQLExpress;Initial Catalog=InstanceStore;
Integrated Security=True;Asynchronous Processing=True"/>
          
<!--Set the TimeToUnload to 0 to force the WF to be unloaded. To have a durable delay, the workflow needs to be unloaded-->
<workflowIdle timeToUnload="0"/>

第一行将持久性行为配置为使用 SQL Server 实例存储。第二行指示持久性框架在实例进入空闲状态时保存实例并将其卸载(这意味着当您执行 Receive 并进入空闲状态等待响应时,工作流实例将被卸载并被保存到数据库中)。

如果工作流配置为持久的并且配置了关联,则主机 (WorkflowServiceHost) 负责在消息到达时基于关联信息加载正确的实例。

 假定您拥有一个配置为关联 customerCode 的实例,假设 customerCode = 43。您向抵押供应商询问利率,在等待响应时实例被保存(持久性包括保存关联信息)。当抵押公司为 customerCode = 43 发送回消息时,WorkflowServiceHost 自动从实例存储中加载该实例并发送消息。

请注意,默认情况下,未安装 SQL 实例存储。您需要通过运行一组随 .NET Framework 4 提供的脚本显式安装它。

跟踪服务

我拥有一个长时间运行的服务,其采用基于消息的方式与其他服务进行通信。此服务配置为持久的,并行执行其许多工作,并且可执行异步活动。这似乎非常复杂。如果出现问题,该如何处理?如何了解哪个活动失败?如果您只想知道服务实例的情况,该怎么办?

Visual Studio 允许调试工作流(您可以通过设置断点在 WF 设计器中进行逐步调试,就像在代码中执行一样),但是这不适用于生成环境。

WF 包括一个丰富的跟踪基础结构,可提供有关正运行的工作流的数据。跟踪会将工作流中发生的情况(跟踪事件)告知跟踪参与者,然后跟踪参与者保存这些事件。跟踪配置文件允许筛选跟踪参与者接收的事件,以便其只获取需要的信息。

WF 4 提供一个将数据保存到 Windows 事件日志中的跟踪参与者 (EtwTrackingParticipant)。您可以通过扩展 TrackingParticipant 创建自己的跟踪参与者。对于此工作流,我使用了默认 EtwTrackingParticipant。您无需编写任何代码即可使用它;只需在 web.config 文件中提供适当的配置。我首先将服务配置为使用 EtwTrackingParticipant:

<!--Set up ETW tracking -->
<etwTracking profileName="HealthMonitoring "/>

我还将其设置为使用 HealthMonitoring 配置文件,该配置文件提供有助于评估服务运行状况的事件(参见图 9)。现在,服务提供有关事件的信息,可帮助您监控其运行状况并在出现问题时加以修复。SDK 提供了许多示例,它们显示了如何创建您自己的跟踪参与者以及如何编写故障排除配置文件。

图 9 指定跟踪参与者配置文件

<tracking>
  <profiles>
    <!--The health monitoring profile queries for workflow instance level records and for workflow activity fault propagation records-->
    <trackingProfile 
      name="HealthMonitoring">
      <workflow activityDefinitionId="*">
        <workflowInstanceQueries>
          <workflowInstanceQuery>
            <states>
              <state name="Started"/>
              <state name="Completed"/>
              <state name="Aborted"/>
              <state name="UnhandledException"/>
            </states>
          </workflowInstanceQuery>
        </workflowInstanceQueries>
        <faultPropagationQueries>
          <faultPropagationQuery 
            faultSourceActivityName ="*" 
            faultHandlerActivityName="*"/>
        </faultPropagationQueries>
      </workflow>
    </trackingProfile>
  </profiles>
</tracking>

部署和使用服务

我已经使用设计器创建了一个工作流并将其配置为持久性和跟踪。剩下的唯一任务就是承载并运行此服务。

在开发时,您可在 Visual Studio 2010 中使用内置 Web 服务主机承载服务。为此,您只需运行包含服务的项目即可完成操作。

将服务承载于生产环境只是稍微复杂一些。您可将 WCF 工作流服务承载于 IIS 或 AppFabric 应用程序服务器扩展中。使用 Visual Studio 2010,您可以创建一个可直接导入到 IIS 中的包。如果您决定使用 AppFabric,您将能够利用诸如仪表板之类的功能(其会提供有关您的服务实例的摘要信息),以及查询记录的跟踪。

最后一步实际上是使用服务。在此应用场景中,Contoso 需要一个基于 Web 的界面,以允许用户与服务交互。这表示从 ASP.NET 应用程序使用服务。

您在这里看到的示例 WCF 工作流服务正如您可以采用纯代码编写的任何其他 WCF 服务一样。要使用它,您需要在客户端项目中添加一个服务引用。此服务引用会创建客户端代理,以调用服务。

添加引用后,您即可调用服务。此服务的客户端针对服务中的每个 Receive 活动执行一项操作。图 10 显示了用于请求抵押审批的代码(因此启动一个新的服务实例)。

图 10 使用服务

protected void OnSubmit(object sender, EventArgs e) {
  using (ContosoRealEstate.ContosoRealEstateClient client = 
    new ContosoRealEstate.ContosoRealEstateClient()) {

    string message = "";        
    string result = client.EvaluateMortgage(
      out message,
      this.txtId.Text,
      Convert.ToInt32(this.txtHousePrice.Text),
      Convert.ToInt32(this.txtDownpayment.Text),
      Convert.ToInt32(this.txtYears.Text),
      Convert.ToInt32(this.txtSalary.Text),
      this.chkCreditHistory.Checked,
      this.chkBankrupcy.Checked,
      this.chkLawsuit.Checked,
      this.chkForeclosure.Checked);
    lblMessage.CssClass= result;
    lblMessage.Text = message + " (" + result + ")";

    this.btnSubmit.Visible = result.Equals("Incorrect");
    this.btnMonitor.Visible = result.Equals("Approved");
  } 
}

结束语

正如您所看到,.NET Framework 4 提供了丰富的功能集,可用于通过组装现有组件构建复杂的实际应用解决方案。该架构还提供了扩展点,以根据适应各种应用场景的具体需要定制这些组件。

通过 WCF 工作流服务,您只需组合现有活动并配置服务即可以声明方式描述长时间运行、持久且经过检测的流程,但是您还可以在必要时编写自己的代码。

在本文中,我组合了 WF 4 和 WCF 4 中的许多功能,以构建独立执行许多工作并协调与其他现有服务的对话的服务。我在未编写代码的情况下构建了整个服务,包括新项目(自定义活动)。

利用这些 .NET Framework 4 技术,您可以实现更多目标。

Leon Welicki 是 Microsoft WF 团队的一名项目经理,专注于活动和 WF 运行时。在加入 Microsoft 之前,他曾担任西班牙一家大型电信公司的首席架构师兼开发经理,并且是西班牙马德里萨拉曼卡宗座大学计算机科学研究生学院的外聘副教授。

衷心感谢以下技术专家对本文的审阅:Dave Cliffe、Vikram Desai、Kavita Kamani 和 Bob Schmidt