构建自定义活动时,您必须做的第一个选择是构建简单活动还是复合活动。简单(或基本)活动是逻辑和执行都封装在活动代码内的那些活动。简单活动示例有:Windows SDK 中的 SendEmail 活动或从数据库查询数据的活动。
与此相反,复合活动要依赖于子活动的执行才能实现其目标。您可以按照自己的需求以两种不同的方式创建复合活动。一种方法是构建这样的复合活动,它允许用户在设计时添加子活动,并控制这些子活动的执行。例如,Windows Workflow Foundation 随附的 While 活动允许您添加子活动,然后配置 While 活动的循环功能。While 复合活动主要用于管理其子活动的执行。构建复合活动的另外一种方法是创建从现有复合活动(例如,Sequence 活动)派生的活动并添加一组活动作为子活动以定义可重用组件。
可重用活动(如经理批准活动)有几个阶段最容易使用现有活动集合加以模拟。经理批准活动可以使用诸如 IfElse 之类的活动来判断经理是否在线。如果在线,可以发送一条即时消息;否则,发送一封电子邮件就可以了。此活动封装了请求批准的所有复杂逻辑,请求批准在工作流中并且其本身就是一个工作流。此活动的用户只需在各自的工作流中使用这个复合活动就可以摆脱实际与经理联系时所必须处理的复杂事务与细节。
要开始理解构建活动的概念,最好从一个非常基本的活动着手,然后逐步将其构建为更为复杂的活动。为了说明这些概念,本文使用了两个主要活动:一个是用于在 ASP.NET 成员身份库中创建用户的活动,一个是自定义 Switch 活动,用于模拟与同名代码概念相类似的执行。这两个活动及其他活动的完整源代码可在本文提供的代码下载中找到。
构建基本活动
构建活动在许多方面上都与构建组件相似。毕竟,活动是刚好利用 Windows Workflow Foundation 的功能的可重用组件,该活动在 Windows Workflow Foundation 框架中执行。所以,构建活动的第一步是确定其功能和接口。以 CreateUser 活动为例,其功能是通过 ASP.NET 的成员身份提供程序模型创建用户。接口会需要创建用户所必需的属性:即电子邮件地址、用户名和密码之类的内容。
使用 Activity 基类中的已替换 Execute 方法来编写用于处理基本活动的功能的代码。除了编写特定于活动功能的代码外,作为活动开发人员,您还要负责返回活动的状态。对于基本活动,这几乎总是 Closed 状态。返回 Closed 是向工作流运行时指明,您的活动已完成其执行过程,运行时可以自由安排工作流中的下一个活动。图 1 显示用于在 ASP.NET 2.0 的成员身份系统中创建用户的基本活动。有关活动生命周期以及运行时与活动之间的约定的更详细信息,请参阅先前提到的由 Don Box 和 Dharma Shukla 撰写的文章。
相关属性
为了使活动的使用者能够配置活动的执行,提供活动的属性是非常重要的。有了标准 Microsoft® .NET 属性或字段(如前例所示),活动的使用者必须在工作流中编写代码以设置这些属性的值。但假设存在这样一种情况,您想要为技术水平较低的人员提供一种构建工作流的机制。您不希望所有用户只是为了将数据从一个活动连接到另一个活动就不得不编写代码。声明性 Windows Workflow Foundation 模型使您能够更加轻松地处理这些情况,支持这一模型的其中一个功能便是相关属性。
相关属性是存储活动值或工作流状态的一种方式。与将值存储在类本身中的标准属性不同,相关属性的值存储在由 DependencyObject 基类维护的字典中。
相关属性支持多个关键功能,但其中最重要的可能就是称为活动绑定的功能。如同名称所暗示的那样,活动绑定就是将一个活动的属性绑定到另一个活动或工作流本身的属性的操作。将相关属性绑定到另一个属性时,其值会自动传播。例如,如果工作流为即将创建的用户名定义了一个属性,而您已将 UserName 属性定义为相关属性,则您可以将 UserName 属性绑定到工作流的该属性。当使用参数启动工作流时,传递到工作流的值将通过绑定自动传递到您的活动。图 2 描述了这种关系。
图 2 使用相关属性进行绑定
这种绑定为组件带来了功能强大的模型,而相当多的人只有依赖于 UI 开发才能实现该同一模型。活动绑定与 ASP.NET 和 Windows Forms 中的数据绑定非常类似,它使得连接组件变得更为简单。它还会使非程序员在构建工作流时变得更加愉快。在正确设计活动之后,用户应当能够将它们拖放到设计图面上并连接活动之间的值,而不必编写任何代码。
在活动中定义相关属性分为两步。第一步包括指定和注册相关属性定义,方法是创建一个静态变量,然后调用 DependencyProperty 类的 Register 方法来设置该变量的值。第二步是定义一个包含 get 和 set 方法的标准 .NET 属性,其中您依赖基类来存储或检索相关属性的值。请注意这里有一段 Visual Studio® 2005 代码,它将这一过程变得更为简单。图 3 中的代码显示重定义为相关属性的 UserName 属性。
还请注意,在本例中,该 .NET 属性本身具有用以指示该属性应如何在属性浏览器中显示的标记。这是 Visual Studio 设计器中最引人注目的地方,当突出显示活动时,其类别和说明将自动显示出来。这些属性都是标准的 .NET 设计器属性,在 MSDN® 库中进行了详细的说明。
活动绑定是通过使用相关属性启用的一项功能。其他功能包括属性提升、更改通知和状态管理。由于属性值存储在中央字典中,活动状态更容易被序列化。同样,利用这个中央存储库能够更加方便地监控这些属性的更改,从而允许其他活动或工作流代码注册更改并在修改特定值时采取行动。
一个常见问题是,开发人员应在何时使用相关属性来代替标准 .NET 属性?答案非常简单,那就是对于任何会因为绑定到另一个属性或字段而获得好处的属性,都应该使用相关属性。事实是,没有不使用相关属性的更好托辞,另外,有了代码段的支持后,很容易将它们编写出来。
构建复合活动
虽然基本活动具有很大价值,但您常常会希望提供更为复杂的可重用组件。在可重用组件中,其中一种是可重用活动,它包括几个基本活动以代表可重用交互或逻辑流。例如,在创建用户之前首先请求经理批准的活动就是一个复合活动。包含在这一个活动中的内容有请求批准、接收响应,然后是创建用户。构建此复合活动的目标是允许用户将其添加到工作流中而不用担心批准过程的复杂性和它的工作方式。
这种简单的复合活动重用了一个现有基类(通常是 SequenceActivity 类)的执行控制。这种情况很常见,以至于 Visual Studio Extension for Windows Workflow Foundation 附带了一个针对此类活动的可视设计器。只需将新活动添加到您的项目便可以开始从 Sequence 活动派生的复合活动,并且能够在可重用组件中定义各个阶段,就像定义工作流一样。使用属性浏览器,您可以将基类更改为任何其他复合活动以更改执行语义,但继续使用设计器构建活动逻辑。图 4 显示用于基于 Sequence 活动创建新复合活动的活动设计器。
图 4
封装活动时,需要将所含活动的属性提供给复合活动的使用者。在上一个示例中,该复合活动的用户需要能够为用户名、电子邮件地址、角色以及正确配置所含活动所需的其他详细信息提供值。Visual Studio Extensions 提供了一种称为属性提升的机制,使得能够更加轻松地做到这一点。使用复合活动设计器时,如果右键单击某项活动,上下文菜单中便会包含用以提升该活动可绑定属性的选项。如果选择此选项,则相关属性便会在复合活动中声明,同时所含活动的属性会绑定到这些值。这样就能向您提供需要设置的属性,而不用强制您编写代码连接这些值。您可以决定您的复合活动使用者会调用所提供的哪个属性并可以继续隐藏活动的实现细节。
如果希望对子活动的执行加以更多控制,请编写一个派生自 CompositeActivity 的活动,然后在 Execute 方法中处理子活动的执行安排。例如,假设存在 Switch 活动。.NET Framework 3.0 中提供的 IfElse 活动为有条件执行活动的单一分支提供了模型。Parallel 活动为执行所有分支提供了模型。只有 ConditionedActivityGroup (CAG) 活动为有条件执行多个分支提供了模型。Switch 比 CAG 更简单,它提供了一个良好的机制来显示如何控制子活动的执行,从而能够根据规则或代码决策有条件地执行活动的多个分支。图 5 显示要用于工作流中的 Switch 活动。请注意,该活动的每个分支都有一个名为 Condition 的属性,可将其设置为声明性规则条件或代码条件,如同 IfElse 活动的某个分支的条件一样。仅当该条件存在且判断其值为 True 时,该活动分支才会执行。
作为复合活动,Switch 活动由许多可有条件执行的序列组成。因此,第一步就是创建另一个活动,即 SwitchBranch 活动,它派生自 Sequence 活动并定义了一个名为 Condition 的 ActivityCondition 属性。就是这个属性允许用户设置用以指示相应序列是否应执行的代码或声明性规则条件。SwitchBranch 充分利用了它的基本活动,同时没有提供其自身的执行逻辑。
在 Switch 活动的 Execute 方法中,将检查每个已启用的子活动并判断相应的条件。如果条件已设置并判断其值为 True,则必须安排执行该子分支。通过调用 ActivityExecutionContext 中的 ExecuteActivity 方法来实现这一点。这是复合活动之间用以区别管理子活动的方式时最常使用的方法。无论您是希望以并行方式执行、按顺序执行还是以相反顺序执行,您都能通过 Execute 方法来识别执行计划以及应该执行哪些子活动而不应该执行哪些子活动。
Switch 活动的 Execute 方法会向运行时返回 Executing 状态。这表示该活动已安排执行其子活动但尚未完成。这会使运行时不再执行下一个活动,直到向它通知此活动已完成。当所有子活动均已完成时,Switch 活动必须能够将已完成信息通知给运行时。因此,在安排执行子分支之前,Switch 活动需要注册活动的更改通知,以便能够知晓子活动的结束时间。图 6 显示用于注册此通知的代码。请注意,此更改通知利用了先前提到的可监控相关属性的更改的能力。活动只有实现 IActivityEventListener 接口才能获得这些更改的通知。
在 IActivityEventListener 接口的 OnEvent 方法中,当活动得知某个子分支已结束时,它会查看是否还有需要执行的任何其他子活动。如果没有,则活动可以请求 ActivityExecutionContext 将自身停止。这与从 Execute 方法返回 Closed 并让运行时知道该活动已完成其所有任务相同。这是在每个活动必须确定其完成自身工作所依据的具体条件时各活动之间加以区别的另一种常见方法。
对于应如何执行子活动以满足创建者和业务案例的需求,每个复合活动可能都会有不同的语义。此处所示的示例只是为了在活动开发者创建复合活动时能够更好地了解所关注的主要问题。在您构建活动时一定要牢记这些内容,并判断该示例是否适用于您的特定情况。
异步和基于事件的活动
虽然有些活动能够以简单的同步方式来完成各自的工作,但在许多情况下,活动会先开始一部分工作,然后等待响应。同样,您可能需要创建一个活动,它可以侦听外部事件,如即将创建的新文件或即将触发的 Windows Management Instrumentation (WMI) 事件。鉴于在运行时中管理工作流的方式,您不能直接在您的活动中为外部资源创建事件处理程序。例如,如果您在活动中针对对文件系统的更改注册了一个事件处理程序,则当工作流需要进入空闲序列化状态时,您的处理程序会与其失去联系。从而造成所引发的任何事件都无法到达您的活动。
Windows Workflow Foundation 提供了基础结构,可帮助解决与当前可能保持的工作流之间的通信这一问题。该通信模型构建于一个队列系统之上:活动通常注册以接收关于队列的消息,而宿主应用程序中的服务则发送关于队列的消息。您的自定义活动可以使用此模型来处理外部事件,也可以传递异步活动执行的完成。这样,您的活动可以先执行到某一点,然后等待激发因素的到来以便继续执行。请看一下图 7,它描述了宿主应用程序中的代码与工作流中的代码(或活动)之间的通信模型。
图 7 与活动异步通信
为了您的活动能够侦听消息是否到达某个队列,您需要首先确保该队列存在。如果它不存在,则必须进行创建。这通常在活动的 Initialize 或 Execute 方法中实现,还要取决于队列希望在何时能够接收到消息。WorkflowQueuingService 提供了创建、查找或删除工作流队列所需的方法。最后,您的活动必须注册才能接收到这些通知,方法是在工作流队列自身中注册 QueueItemAvailable 事件。在确保队列存在并注册事件后,当队列中有可用项目时,您的活动会得到通知,之后,您可以从队列中取出该项目并对其进行处理。
创建异步活动时,请使用我刚刚介绍的步骤来准备您的活动,使其能够接收到关于队列的消息,当您的异步活动完成时,请将一条消息送入队列以通知给您的活动,还可以选择向它发送结果数据。由于 OnEvent 方法通过 Sender 参数来接收对 ActivityExecutionContext 的引用,所以可通过调用 CloseActivity 方法来结束该活动。
例如,假设存在一个简单的 ReadLine 活动。在本例中,ReadLine 活动将在其执行方法中创建一个队列并注册数据是否到达队列的通知(通过实现 IActivityEventListener 接口来接收所说的通知)。宿主应用程序或自定义运行时服务会通过控制台检索用户输入并针对活动将其加入队列。ReadLine 活动示例随附在本文的代码中,它使用此模式作为其实现。
要创建一个能够充当事件接收器的活动(如 HandleExternalEvent 活动),您还需要实现 IEventActivity 接口。如果您的活动要侦听事件,则此接口用于定义该活动的主要职责:
public interface IEventActivity { void Subscribe(ActivityExecutionContext parentContext, IActivityEventListener<QueueEventArgs> parentEventHandler); void Unsubscribe(ActivityExecutionContext parentContext, IActivityEventListener<QueueEventArgs> parentEventHandler); IComparable QueueName { get; } }
QueueName 属性必须返回 IComparable 值,消息加入队列时,它可以唯一地标识您的活动。对于用于将消息加入队列以通知工作流运行时的代码,也需要使用这同一个队列名。
通过此接口,能够命令活动在其执行前订阅事件并让活动知道何时取消订阅。在订阅和取消订阅方法中,该活动负责确保使用 QueueName 来创建队列并在处理结束时删除队列。此外,这也为您的活动能够向任何本地服务注册信息提供了机会,这些本地服务将代表活动来执行逻辑并通过将消息加入队列予以响应。
本地服务是您定义并从主机添加到工作流运行时的一个类,它可以被您的宿主代码、工作流或您的活动所利用。只要宿主应用程序处于运行状态,本地服务就能够维护事件处理程序或其他侦听程序,从而可通过将消息加入队列来确保相应的数据到达工作流。您传递给本地服务的信息应包括队列名和工作流实例 ID 的相关信息,以及该服务发起工作或向您的活动返回结果时所需的任何信息。
本文的示例代码包括一个自定义事件活动,它用于创建队列及注册处理程序以在事件到达队列时获得通知。宿主程序只是在一个短暂的延迟之后将项目加入队列来展示如何将项目加入队列;在实际情况中,您的本地服务大多负责发送数据,但这表明宿主也可以为活动将数据加入队列。
使用基于事件的活动的通用模式是在 Execute 方法中检查队列中是否有项目,如果没有数据,则订阅适用于该队列的项目并返回执行状态。然后,在项目到达的处理程序中,从队列读出数据,也可以选择在结束您的活动之前引发该活动中的任何事件。
通过实现这些接口,现在,您的活动可以按照不同的方式参与到工作流中。例如,在状态机工作流中,您的活动现在可以是在事件驱动的序列中作为第一个活动出现的事件,从而允许您的事件触发一系列活动,并可能转变为新状态。有关这种类型活动的完整示例,请看 Windows SDK 中的 FileWatcher 活动。
处理错误
活动开发人员的另一项职责是处理错误条件。活动中的错误管理有许多特性与任何其他代码一样,但对于管理活动执行,还要考虑一些特殊注意事项。当活动执行逻辑中发生错误时,您应引发一个异常,就像对待任何其他组件那样尽可能提供所需的一切详细信息。这会向运行时发送一个信号:已发生一个错误,它可以与工作流中的活动交互以使这些活动彻底关闭,运行时也可能会调用特殊处理程序来处理这些错误。
只要您的活动执行代码中引发异常,就会调用活动的 HandleFault 方法。在 HandleFault 方法中,您应编写释放任何资源或取消您的活动可能已经发起的任何异步操作所需的代码。请注意,该异常仍将触发到运行时;此方法只是允许您的活动在出现该方法后加以整理。复合活动还支持 FaultHandlers,它是一个特殊的附加子活动,在复合活动从 faulting 状态转变为 closed 状态之后作为便于后处理的手段而执行。您可以使用 FaultHandlers 视图来模拟如何处理子活动所引发的错误,从而能够在工作流中使用更好的错误处理代码来处理特定于该问题的异常。
Activity 类中定义的 HandleFault 方法的默认行为是调用活动的 Cancel 方法。这样能够集中整理资源,但这可能并不适用于所有情况,因为根据所引发异常的类型,您的活动可能有特定的资源需要整理。如果您没有处理资源的特定需要,请执行其他记录操作或整理操作,随后,在大多数情况下,默认行为会替您工作。
请注意,通常,取消操作用于实现这样的语义:一个复合活动已衍生出许多子活动,为了实现其所需的语义,它需要取消一部分子活动正在进行的执行操作。取消是积极的操作,它用于支持向前执行,而错误是消极的,它用于实现异常情况。取消和错误处理是两个完全不同的操作,HandleFault 的基类实现调用 Cancel 方法只是为了支持非常基本的情况。
设计体验
许多活动设计用于基于模型的用户界面(如 Visual Studio Extensions for Windows Workflow Foundation 随附的设计器)中。在许多方面上,活动的设计体验比执行要素更重要,因为对于其他开发人员和活动的使用者而言,它是最为直观的。在许多不同的情况下(而不仅仅是在开发时),Windows Workflow Foundation 都会允许工作流可视化,因此您的活动不仅要传达其职责的语义,而且也要表明它看起来对用户是合乎逻辑且有用的,这非常重要。
设计时体验有几个组成部分,包括外观、交互性和验证。这些设计组成部分本身便是 .NET 类,并通过应用于活动类的自定义属性与活动相关联。第一个这样的类是活动设计器,它为核心开发人员创建丰富的设计时体验提供入口点。该设计器类允许您通过与属性浏览器、上下文菜单、鼠标点击与移动,以及活动在设计图面上的实际绘图之间的交互来参与设计体验。
您通过创建从 ActivityDesigner 基类或 Windows Workflow Foundation 库中可用的任何数量的复合活动设计器派生的一个类来创建设计器。以下是应用于 Switch 活动的 SwitchActivityDesigner:
[Designer(typeof(SwitchActivityDesigner), typeof(IDesigner))] public partial class SwitchActivity : System.Workflow.ComponentModel.CompositeActivity, IActivityEventListener<ActivityExecutionStatusChangedEventArgs> ...
我将重点介绍在自定义设计器中最常使用的几个关键功能。其中的第一个功能是能够提供针对您的活动的自定义上下文菜单项,使用户能够发起某项操作。通过覆盖设计器中的 Verbs 属性,可以指定应添加到活动的上下文菜单中的菜单项集合。这样就允许用户通过右键单击或其他方式来打开上下文菜单,然后发起操作。对于添加到集合中的每个动词,都要提供一个事件处理程序,当用户在菜单中单击相应的项时会调用该事件处理程序。此外,需要传入 DesignerVerbGroup 枚举的一个成员,以指示您希望在上下文菜单中如何对特定动词进行分组。这样便为您提供了根据目的来分组相关活动的能力,如编辑或设置选项。在 Switch 活动的示例代码中,添加了一个动词以允许用户重置子分支上的所有条件。以下是将该动词添加到上下文菜单中的代码:
protected override ActivityDesignerVerbCollection Verbs { get { ActivityDesignerVerbCollection newVerbs = new ActivityDesignerVerbCollection(); newVerbs.AddRange(base.Verbs); newVerbs.Add(new ActivityDesignerVerb(this, DesignerVerbGroup.Edit, "清除所有条件", new EventHandler(ClearConditions))); return newVerbs; } }
除 Verbs 属性外,您还可以覆盖 DesignerVerbs 属性,它允许您将动词添加到错误上下文菜单中以指示当前配置中存在的问题。最后,您还可以覆盖 SmartTagVerbs 属性,以将项目添加到智能标记,您会在大多数复合活动的名称下找到智能标记。通常仅当您需要提供设计图面的替代视图(类似于取消和错误处理程序视图)时才使用此项目。
也许,活动开发人员最常见的设计时需要就是能够在设计图面上自定义活动的外观。Windows Workflow Foundation 为创建您所需的外观提供了多种机制:从仅仅设置几个属性的非常简单的方法到允许您使用 System.Drawing.Graphics 对象直接绘制活动的最强大的方法。我将首先介绍最简单的方法,在许多情况下,它们足以满足您的需要。
就工作流活动的外观而言,需要掌握的第一点是它参与到设计器主题当中。主题只是用于定义每个活动在设计器中的外观。如果您安装了 Visual Studio Extension for Windows Workflow Foundation 并在“Tools”(工具)菜单中选择了“Options”(选项),您将看到工作流设计器的一个类别,其中显示了两个已安装主题。单击“New”(新建)按钮,您会了解每个活动的可用属性。您可以保存主题并将其应用在 Visual Studio 设计器中,但如果您要在您自己的应用程序中重新托管工作流设计器控件,您还可以使用它。通过使用主题,您可以完全更改工作流设计图面及设计器中托管的每个活动。
设计器的默认主题用于检查活动并允许它提供主题信息。在您的活动开发过程中,您通过使用一个属性将自定义 DesignerTheme 与您的自定义设计器相关联来实现这一点。然后,在您的设计器主题的构造函数中,您可以为 BackColorStart、BackColorEnd、ForeColor 和 BackgroundStyle 等属性设置值,以更改活动及其文本的着色。CreateUserActivityDesignerTheme 提供了一个更新这些值的示例:
public class CreateUserActivityDesignerTheme : ActivityDesignerTheme { public CreateUserActivityDesignerTheme(WorkflowTheme theme) : base(theme) { this.BackColorEnd = Color.BurlyWood; this.BackColorStart = Color.Bisque; this.BackgroundStyle = LinearGradientMode.ForwardDiagonal; this.ForeColor = Color.Black; } }
除了颜色和设计,您还可以指定在活动的默认位置画一个图标。通过使用 ToolboxBitmap 属性及指定作为嵌入式资源构建到程序集中的一个图像文件(通常为 16×16 位图或 PNG 文件)将图标与活动连接起来。除了在设计器中使用该图像外,当手动或通过安装程序将活动添加到工具箱时也显示这个图像,这使得给定的属性名有意义。图 8 显示了显示有主题和图标支持的工作流内部的 CreateUser 活动。
创建复合活动的设计器时,除了要处理主题和图标的问题外,您可能还要必须在您的子活动设计器之间绘制连接器,否则请更改如何包含这些子活动设计器的外观。但是,有几个基类可能会提供编写代码时所需的功能,以便减少您必须编写的代码量。例如,对于 Switch 活动,设计器从 ParallelActivityDesigner 派生,ParallelActivityDesigner 用于将每个子活动绘制为分支并创建设计器动词以添加新分支。Switch 活动设计器只是必须覆盖一个方法并返回一个新的 SwitchBranchActivity。对于您拥有的带有分支的任何复合活动,此设计器会被证明很有用并被 IfElse、Parallel 和 Listen 活动用于内部。
ParallelActivityDesigner 类的基类是 StructuredCompositeActivityDesigner 类,当您的复合活动包含其他设计器时,StructuredCompositeActivityDesigner 类为连接器提供大量的呈现逻辑。StructuredCompositeActivityDesigner 的一个同级类是 FreeFormActivityDesigner。此设计器是 StateMachine 工作流设计器的基础,当可在复合活动中随意放置子活动(与结构化放置相反)时使用。将这两种设计器中的一种用作您自己的设计器的基础。
最后一个注意事项:当您使用活动设计器创建复合活动时,该活动会在设计图面上出现并呈现出它的所有子活动。因此,您的复合活动的使用者会在您的实现中看到所有内部活动。这可能是您想要的,但如果不是这样,您可以通过为您的复合活动创建一个从 ActivityDesigner 基类派生的设计器来解决这一问题。这样便实现了简单呈现,即仅呈现一个活动。您可以附加设计器主题和 ToolboxBitmap 属性以进一步自定义外观,从而提供真正的封装。
验证
在创建活动或任何相关组件时,验证用户输入并确保组件配置正确是一种很好的做法。事实上,如果您能在一开始就阻止用户,使其不会错误地配置您的组件,那样会更好。您可以在以下两个主要开发方面确保活动能够得到正确配置:在设计器中以及针对您的活动使用自定义验证程序。
在为您的活动创建自定义设计器时,请控制可以添加到复合活动中的那些活动或者可将您的活动添加到其中的那些活动。请注意,这一行为仅适用于设计环境,如果您的活动是通过代码添加的,则不起作用,这一点很重要。
通过覆盖 ActivityDesigner 基类中的 CanBeParentedTo 方法,您可以过滤能够将您的活动添加到其中的那些活动的类型。如果您从该方法返回 false,则用户会看到一个红色的斜杠,这是代表 No 的国际符号,表示不能添加您的活动。例如,SwitchBranch 活动仅允许它自身成为 SwitchActivity 的父项,从而确保它只在正确的上下文中运行。通过覆盖 CompositeActivityDesigner 类中的 CanInsertActivities 方法,您能够控制是否可以将某个特定活动或某一组活动添加到您的复合活动中。利用这两个方法,您能够控制与您的活动相关的活动结构。当您开始审视活动的执行需求时,这一点变得非常重要,因为它能确保您进行设置以便成功执行。
验证您的活动的另外一个方法是创建自定义验证程序。Validator 类负责检查您的活动并确保根据您的要求进行了配置,此验证始终会发生,而不管您是否是使用设计器将活动添加到工作流中的。现在您很可能猜测出应该如何创建验证程序:创建一个从 ActivityValidator 派生的类,然后覆盖 Validate 方法。然后,使用 ActivityValidatorAttribute 将您的验证程序连接到您的活动。
CreateUser 活动有一个属性用于保存密码。但是,在工作流中静态配置密码并不是一个好主意。相反,验证程序组件会确保将密码属性绑定到另外一个值,以便能够在运行时设置该属性的值。图 9 显示用于测试该属性并确保它已进行了绑定的 CreateUserValidator 类。
代码首先会验证正在被验证的活动的类型是否正确,以及它是否是在上下文中而非自身的编译环境中进行验证。如果您不检查父属性是否为 null,则您将不能在一开始就构建您的活动,因为在编译您的活动时验证程序会运行。ValidationErrorCollection 用于将错误集合传回设计器,以便将相应的错误消息添加到用户界面中。
结论
正如您所看到的,Windows Workflow Foundation 为您构建可在工作流中使用的可重用的、易于设计的活动提供了丰富的环境,而无论这些活动是叶活动(是实现域特定逻辑的关键)还是复合活动(是创建反映实体间实际交互的控制流模式的关键)。事实上,无论是任何现成的活动还是您编写的自定义活动,Windows Workflow Foundation 都同样对待。
从活动执行到设计体验,您能够访问的资源与构建基活动库的 Microsoft 小组所能访问的资源是一样的。构建健壮的活动使您能够将您的应用程序和服务提供给 Windows Workflow Foundation 的处理模型并能够利用该模型必须提供的所有服务。我鼓励您利用社区网站 (wf.netfx3.com) 来查找活动以及在构建您自己的活动后提交这些活动。