【翻译】WF从入门到精通(第四章):活动及workflow类型介绍
上一篇:【翻译】WF从入门到精通(第三章):workflow实例
学习完本章,你将掌握:
1.workflow活动是怎样形成的
2.顺序工作流和状态机工作流之间的区别
3.创建一个顺序工作流
4.创建一个状态机工作流
活动是WF中workflow处理的基本单位,假如你再把一个业务处理过程(或workflow任务)进行分解,你会发现它由更小、更细的任务组成。假如需设计这样一个大的任务,它需把信息送到一系列的数据处理系统进行处理,那么子任务或许就包括这样一些事情:从数据库读取数据,使用这些数据生成一个文件,通过使用FTP或XML Web service把文件传到一个远程服务器上,标记信息已经被处理(如通过写入数据库并进入审计步骤),等等。这些子任务都聚焦到一个特定的任务上:读数据库、上传文件、进行审计。换句话说,它们是活动。
当你创建workflow时,你会把这些单独的活动捆在一起,并让活动从一个转到另一个。一些活动可作为其它活动的容器。一些活动执行一个单一任务,这我们已谈过。基于容器的活动用来容纳其它活动,在前一章中我们谈及的root活动就是这种活动。root活动既可是一个顺序活动也可是一个状态机活动,本章中我们将对这些活动的类型进行说明。
活动怎样知道在本步骤完成后下一步将做什么呢?本章将主要把焦点放在这上面上。或许活动会以你创建一个root活动时指定的顺序执行,或者可能是仅在一个特定的事件发生后才去执行一个指定的活动。为了让我们更好地理解活动,我们首先要去看看WF Activity对象,然后看看活动是怎样链接在一起的。
活动介绍:基本的工作单位
WF为你提供了一个Activity对象。Activity实现了一个看起来很简单的基类。它不会做许多智能任务,但它可进行workflow的相互交互(这可不简单)。活动对象由“Activity”派生,提供出了强悍的功能。你可自如地创建你自己的活动,这个话题将在第13章(自定义活动)进行介绍。事实上,本书的第二部分都是在介绍活动(第7章至第13章)。表4-1列出了许多我们通常感兴趣的Activity的属性,表4-2列出了你会经常用到的方法。在第13章,你还会看到更多的和自定义活动相关的方法和属性。
表4-1 活动(Activity)的属性
属性 | 功能 |
Description | 获取或设置用户定义的关于活动的描述。 |
Enable | 获取或设置一个指明实例能否被执行和验证的值。 |
ExecutionResult | 获取实例最后运行的结果(ActivityExecutionResult)。(有Canceled、Compensated、Faulted、None和Succeeded)。 |
ExecutionStatus | 得到workflow的状态,其为ActivityExecutionStatus的枚举值(Canceling、Closed、Compensating、Executing、Faulting和Initialized)之一。 |
Name | 获取或设置活动实例的名称。 |
Parent | 获取包含本活动的父活动。 |
WorkflowInstanceId | 获取和该活动相关的workflow实例的标识符。 |
表4-2 活动(Activity)的方法
属性 | 功能 |
Cancel | 取消活动的执行。 |
Clone | 返回活动的一个深拷贝。 |
Execute | 以同步方式运行活动。 |
GetActivityByName | 假如在一个组合活动上执行,本方法将返回组合活动中所包含的指定名称的活动。 |
Load | 从一个流中加载一个活动的实例。 |
RaiseEvent | 触发一个和指定的依赖属性相关的事件。 |
RaiseGenericEvent<T> | 触发和所引用的依赖属性相关的事件。RaiseEvent和RaiseGenericEvent的作用是一样的——第一个事件RaiseEvent直接指出DependencyPropenty,而RaiseGenericEvent则是一个泛型版本。 |
Save | 把活动保存到流中。 |
活动的方法通常都具有虚拟和受保护的属性。目的是你可去覆盖它们,使其提供一个符合你自己的活动所需要的实现。目前为止,最关键的方法是Execute。当workflow运行时调用这个方法时,你的活动便开始执行了。
活动可被分为两个大类:组合活动和基本活动。组合活动包含其它活动。一个极好的例子是我们目前为止贯穿书中的Sequential活动(译者注:它是基于顺序工作流中所有活动的载体,在创建一个顺序工作流时Visual Studio就已为我们创建好了,可在视图设计器中看到)。目前为止所有的程序实例执行workflow实例的方式都是Sequential活动,它包含其它活动,如它自身、Delay活动和Code活动。
基本活动,就像我刚谈到的Delay活动和Code活动,它们是一个基于单一任务的活动,我在本章早些时候谈过它。最终,你需要基本活动去实际承载特定的任务。组合活动或许可指挥任务和数据的流动,但基本活动能做更多。
ActivityExecutionContext对象
许多Activity对象的方法需要一个ActivityExecutionContext对象来进行输入。在workflow运行时把你要执行的workflow实例入队的时候ActivityExecutionContext对象被创建,因此,它不是你直接要创建的对象。workflow运行时为你创建它。
ActivityExecutionContext对象的作用是提供活动以方法和服务,以便和workflow实例挂钩。这些如初始化,定时器和产生执行流。它本质上是一个helper对象。在13章将更详细的对活动上下文(环境)进行讨论。
备注:假如你熟悉ASP.NET编程的话,这个context对象本质上和System.Web.HttpContext对象的作用是一样的。其它相似的还有System.Threading.Thread.CurrentContext。所有这些Context对象的目标都是一样的:提供一个存储位置并容易地恢复一个当前执行实例的信息。这种情况下,它是一个执行当中的活动的一个实例。
依赖属性(Dependency Properties)
在表4-2中,你将看到一些依赖属性(DependencyProperty)。什么是依赖属性呢?
通常,假如你为类创建了一个属性的话,你也会在类中创建一个字段来存储该属性的值。普遍的代码如下:
{
protected Int32 _x=0;
public Int32 X
{
get { return _x;}
set { _x = value; }
}
}
然而,WF和WPF通常都非常需要去访问你类中的属性。WPF需要指明容器中控件的空间和大小以便能最佳地被render。WF需要依赖属性来方便地进行活动绑定。WF中ActivityBind类可为你方便地进行活动绑定。
活动验证
活动通常都具有验证能力,你可回忆第一章。
在第一章的例子中有这样一种情况,如在IfElse活动中未指定应该选择哪一个分支进行执行的条件时,Visual Studio会提醒我们。其它活动实现了不同的验证算法。假如我们编译带有验证错误的代码,我们的编译都会失败。我们必须纠正这些验证条件不充分的代码,才能编译和执行我们的workflow代码。
workflow类型
你已创建过workflow应用程序,因此你可能注意到可以创建不同类型的workflow应用。
workflow应用程序的类型很大程度上依赖于你选择的root活动。
尽管你注意到在新项目对话框中仅仅只有两种workflow类型的应用程序可供选择,
但实际运用中存在三种主要的类型。迄今为止本书中你已经创建过顺序工作流,因此它们并不神秘。
当你创建workflow时,你的活动以你规定的顺序执行。
另一种从新项目对话框中看到的workflow类型是状态机工作流。我将在本章讨论它的更多细节。
第三种workflow类型基于顺序工作流,但它是规则驱动的。它不是仅仅执行你指定的任务,
而是由Policy活动和规则条件组成的基于规则的workflow,来执行基于你指定的业务规则workflow任务。
我们将在12章更多地学习这种workflow类型:“Policy活动”。因为这种类型的workflow以顺序活动作为root,
因此在新项目对话框中没有这种类型的workflow应用程序的模板可供选择。你应以顺序工作流作为起始,
然后增加基于规则的活动。
选择一种workflow类型
在什么情况下一种类型的workflow比另一种类型的workflow更好?你如何选择合适的workflow类型呢?
表4-3可为你提供一些基本的参考。
表4-3 选择基本的workflow类型的判定表
workflow类型 | 适用条件 |
顺序工作流 | workflow任务可以自治的执行,很少由外部进行控制。主要由workflow自身来对执行的任务进行控制。只有少量用户或没有用户来和它进行交互。它的root活动是SequentialWorkflow活动。 |
状态机工作流 | workflow任务严重依赖外部来控制和指示其执行。预期有很多的用户交互(或其它外部控制)。对于基于状态的workflow,root活动是StateMachineWorkflow活动。 |
基于规则的工作流 | 业务逻辑中包含复杂的判断条件,既不像顺序工作流也不像状态机工作流。基于规则的工作流或者有一个顺序的root活动,或者有一个基于状态的root活动。 |
假如你需要大量的用户交互,当你的workflow发送通知给用户或其它系统(有各种原因:通知、需要批复、选择一个选项等等)以使用户或其它系统进行响应(它们的响应来自事件)时,状态机工作流可能是更好的选择。这些事件触发了workflow从一种处理状态转化到另一种处理状态。我将在本章后面及14章(“基于状态的工作流”)更多地讨论这些。
最后一种workflow类型(我们将在12章看到)是基于规则的workflow。这些workflow基于业务规则判定是否进行转化,并判定转化后的目标是什么。这些workflow通常都预置了更加复杂的剧情。
你或许会认为所有的workflow都能以基于规则的工作流类型来创建,但我们通常并不总是使用这种方式进行创建。因为其它的workflow类型,如顺序工作流和状态机工作流,它们能更容易地创建和测试。
要用最合适的workflow类型来构建你的系统。通常,在许多真实案例中你会发现你自己使用了所有三种workflow类型的组合。
顺序活动
让我们进一步深入顺序复合活动吧。尽管迄今为止我们使用这些活动贯穿本书,但我在之前有意地拖延谈论关于它的更多内容。现在我们去理解了workflow运行时和workflow实例是怎样工作的,并且知道workflow实例是我们正运行中的workflow活动的版本,我们能更好的了解发生了什么。
执行顺序活动意味着这些活动以一个指定的顺序执行。首先要做的事最先执行,最后才做的事最后执行。一个顺序活动就像在根据目录执行。你需要记下首先要做的任务,接下来要做的任务和最后要做的任务。假如这些任务以顺序活动的方式存储,WF将以你指定的顺序精准地执行每个任务。
` 备注:本书中我们不会看到以动态的方式添加活动,但你应知道这是可以做到的。
在Visual Studio中,workflow的视图设计器可帮你展示你的workflow。当你创建一个顺序工作流应用程序并在设计器中打开root活动时,你可把任务放到屏幕的最上方以便首先被执行。那些朝向底部的任务将晚些执行。从可视化界面可看出,活动运行的顺序是从上到下。当然顺序活动还可以是一个复合活动。
创建顺序工作流
在本书中迄今为止我们已创建过一些顺序工作流应用程序,因此这里我不再创建它们。但我还是把完整的步骤重复一下。
建立一个顺序工作流应用程序
1.打开Microsoft Vistual Studio 2008。
2.在文件菜单上,选择新建项目。然后将呈现新项目对话框。
3.在项目类型面板中,展开Vistual C#树形节点,呈现出基于workflow项目的模板。
4.在模板面板中,点击顺序工作流控制台应用程序或顺序工作流库。前者创建一个可执行的应用程序并以控制台的方式执行,而后者创建一个动态链接库并在其它应用程序中使用。
5.输入你的项目或应用程序的名称。
6.输入或选择你想保存你的项目的所在路径。
7.点击确定,Visual Studio 2008将为你建立一个基本项目,其中包含workflow视图设计器用户界面。
然后,你就可方便地从工具箱中拖拽你需要的活动,调整它们的属性以符合你的需求。假如你需要增加更多workflow库的项目,你可参考我前一章中的描述,或者简单地直接在你的应用程序中增加一个新的workflow类。接下来我们还会看到大量的例子。
状态活动
迄今为止在本书中我们还未看到过状态机工作流。14章完全把焦点放到基于状态的工作流的工作上,但我在这里将介绍一些概念,我们也会快速地创建一个基于状态的工作流。
看看这样一个术语:有限状态机。我们把这个术语分成三个词:有限、状态和机器。有限,意思是我们将进行转化的状态的数目是有限的。状态是我们的应用程序在事件发生时进行转化的逻辑条件。机器则意味自动化。我将用一个例子来阐明。
在工程学校,或许会要求你使用有限状态机来设计一些数字系统。例如自动售货机和洗衣机。看看自动售货机,思考一下机器工作必须具有的步骤,以便它能为你提供你需要的商品(如汽水、糖果、点心等等)。当你投入硬币时,它会合计你投入的硬币金额,直到你投入的硬币金额能购买商品时为止。当你选择一个商品时,它会检查存货清单。假如有货,它就把你选择的东西分发给你。
我们可以使用有限状态机来构建自动售货机。在有限自动机的图示中,我们使用圆来表示状态,箭头来表示状态之间的转换,转换由事件触发。图中有一个逻辑上的起点和一个或多个逻辑上的终点。假如我们停在其它地方,我们的应用程序就被称作未指定状态或者无效状态。我们的工作就是防止无效状态,如我们不能免费地获取商品,我们也不应该接收超过商品价格的多余的钱。假如自动售货机接受了钱但又未提供商品的话,用户毫无疑问会暴怒。
假象一下简化的自动售货机,让我们画出状态和导致状态转换的事件吧。正如我提到的,状态用圆来表示。使你的机器从一个状态变为另一个状态的事件用箭头表示。它们都可命名以便我们知道这些是什么状态和相关的转换。我们毫无疑问需要一个开始状态,如下图:
图4-2 有限状态机起始状态符号
这个状态表示机器所处的这样一个位置:等待有人来投入一个硬币。因此看看当有人来投入一个硬币,但它还不够买一个商品的情况,我们通过创建一个新状态来进行模拟,这个状态叫WaitCoins(等待硬币)状态,通过CoinInserted(投入硬币)事件转换到该状态,如图4-3:
图4-3 转换到WaitCoins状态
在用户投入足够金额的钱以能购买其中的商品之前,机器一直处在WaitCoins状态,并接受CoinInserted事件,否则会触发SufficientCoins(金额足够)事件使我们的机器转到WaitSelection(等待选择)状态。在这里我们的自动售货机会耐心地等待用户选择一个商品。(在实际生活中,用户也能在任何时候要回投入的硬币,为了简单起见,本例还是不考虑它吧。)
当用户选择商品后,商品会被分发给用户,我们的(状态)转换也就结束了。完成状态,或者称作结束状态,由二个圆圈来指明,参见图4-4。
图4-4
尽管这个自动售货机在现实世界中或许太过于简单,但此处只是期望为你提供一个的简要描述,使你明白状态机是如何工作的。当我们设计状态机时,我们指明其离散的状态,或者逻辑位置来等待事件发生,然后我们指明转换机器状态的事件。有些事件可让机器返回到同一状态,如开始状态。其它事件则会在一个新的事件被处理后使机器转换到一个新的状态。没有事件被触发就没有状态的转换,理想情况下应没有无法预料的事件或异常。
这种模型和我们用过的顺序工作流模型有很大的不同。在顺序工作流里,活动以指定的顺序依次执行。一旦在该顺序链上的一个活动执行完它的任务,在该链上的下一个活动就开始执行它的工作。在workflow处理中或许有事件参与其中,但它们在workflow任务的处理(如定时器事件)上相对简单。
但状态机会花费大量时间在等待上。它们等待事件,依赖事件来使它们的状态进行转换。状态自身不会激发事件(尽管它们可能会调用外部代码)。它们就是事件处理,因此它们会耐心地等待它们需要的事件来进行状态的转换。依靠事件,它完全可能从一个状态切换到任何一个离散的不同的状态。假如我们的自动售货机处在WaitCoins状态,当接受一个CoinInserted事件、RefundRequested事件或ImminentPowerdown事件时会分别做不同的事情。但我并未在图4-4中这个经过简化的模型里画出这些事件,但我相信你能看懂不同的事件是怎样驱动你的有限状态机转换到不同状态的。
在WF里,基于状态的workflow内的个别状态由State活动创建。State活动是一个复合(组合)活动,但它对容纳的子活动有限制。你将在14章学习到基于状态的workflow的更多东西。
备注:正如顺序工作流使用一个特别的Sequence活动来容纳整个工作流一样,基于状态的工作流也有一个特别的root活动做这事,这就是StateMachineWorkflow活动,它是一个特别的State活动。特别之处是它是必须的,这样当初始化执行时root活动就能接受初始化参数。
创建一个状态机工作流应用程序
怎样创建一个基于状态的workflow呢?创建一个基于状态的工作流和创建一个顺序工作流一样容易。我们现在就来看看怎样创建一个状态机工作流。然而我们现在不会添加任何代码——虽然本书后面有很多时候需我们这样去做,但我们现在只要需要了解怎样创建一个状态机工作流就行了。
创建一个状态机工作流应用
1.启动Microsoft Visual Studio 2008。
2.在文件菜单上,选择新建一个项目,这将打开新建项目对话框。
3.展开项目类型面板中的Visual C#节点,这将显示所有使用C#语言的项目类型。
4.在Visual C#节点下点击Workflow节点,这将显示所有基于工作流的项目模板。
5.在模板面板内,点击状态机工作流控制台应用程序或状态机工作流库。如下图所示:
6.输入程序或项目的名称。
7.输入或选择项目文件要保存的位置。
8.点击确定。Visual Studio 2008将为你创建一个包含workflow视图设计器用户界面的项目,如下图:
我们需要去触发引发工作流改变状态的事件,因此,我们需理解workflow实例是怎样和它们的宿主应用程序进行通信的。我们将在第八章(“调用外部方法”)看到宿主和workflow之间的通信,在第十章(“事件活动”)中我们将学习状态机工作流的事件驱动。