WF 从入门到精通(第十四章):基于状态的工作流

学习完本章,你将掌握:

1.理解状态机的概念以及它怎样被模拟到工作流处理中的

2.创建基于状态的工作流

3.运用初始(initial)和终止(terminal)状态条件

4.使用代码进行状态的切换


在第四章“活动和工作流类型介绍”中,我阐述过你使用WF 所能创建的工作流类型,

在那里我提到过基于状态的工作流。基于状态的工作流模型被认为是有限自动机(finite

state machine)。基于状态的工作流在工作流需要和外部事件进行许多交互的场合中大出风

头。在事件触发并被工作流处理的时候,工作流能按要求进行状态的切换。

WF 为创建基于状态的工作流提供了富余的开发体验,你迄今为止在本书中看到的许多

东西都适用于基于状态的工作流。例如,当一个状态切换过来的时 候,假如你想的话,你能

去执行几个顺序活动,进行条件判定(使用规则或者代码),或者使用一个迭代活动结构来

循环访问一些数据点。唯一真正的区别是活动怎 样排队执行。在顺序或并行工作流中,它们

以出现的顺序进行排队。但是在基于状态的工作流中,活动以状态切换进出来进行排队。事

件通常驱动这些切换过程,但 是这条规则不是通用的。让我们再看看状态机的概念并把这些

概念和你能使用的WF 活动结合起来去构建你的工作流。

状态机的概念

状态机的目的是构建你业务流程中的离散点,切换通过事件来控制。例如,把你的洗衣

机接通电源,然后关门并按下启动按钮。按下启动按钮时初始化了一个状态机,它通过运行

各种各样的清洁周期来清洗你待洗的衣物直到这些周期全部完成。

状态机有一个已知的起点和一个已知的终点。中间的状态应能通过预期事件的触发去进

行控制,但机器总处于一个特定的状态。有时事件把状态机扔进无 效的状态中,这种情形和

在你的应用程序中维持未处理的异常的情形来说并没有什么不同,整个过程不是忽然停止就

是完全崩溃。无论哪种情况,切换到无效状态都 是要密切监视的,至少在数字电子系统

(digital electronic systems)中是这样。

总的来说,第4 章涵盖了涉及状态机的基本概念。可看看“状态活动”这一节快速复习

一下。让我们从怎样设计活动转到在基于状态的工作流中怎样使用活动去吧。

使用状态活动

也许你不会太惊讶,在你的基于状态的工作流中State 活动构建了一个状态。它是一个

组合活动,但它局限于只接受特定类型的活动来作为它的子活动,它们是:EventDriven 活

动,StateInitialization 活动,StateFinalization 活动以及其它State 活动。EventDriven

活动等待(监听)那些将导致切换到另一个状态的事件,而在状态被切换进来和切换出去的

时候,StateInitialization 和StateFinalization 是保证能分别去执行相应处理的活动。

对于能拖拽第二个State 活动到一个已存在的State 活动中去可能看起来有些古怪,但其意

图是提供一种在父状态机中嵌入子状态机的能力。

对于你的状态能容纳的那些有效活动的数目也有一个限制。只允许有唯一的一个

StateInitialization 和StateFinalization,你可以只有其中的一个,但每一个都不能超过

一个。它们都不是必须的。

但是并没有说你不能只有一个或者更多的子EventDriven 和State 活动。事实上,一般

都能找到多个EventDriven 活动,因为每一个事件可能会导致切换到一个不同的状态。例如,

一个“不批准(disapprove)”事件可能会切换到最终的状态(结束状态),而一个“批准

(approve)”事件则可能切换到一个预定的状态并要求进行更多的审批。至于State 活动,

假如你要创建嵌入的基于状态的工作流的话,毫无疑问超过一个也应当是允许的。只有一个

状态(切换)的基于状态的工作流构建成了一个简单的顺序工作流,因此在那种情况下你应

当直接使用一个顺序工作流。在任何情况下,使用State 活动只需从工具箱中拖拽它的一个

实例到工作流视图设计器上,唯一的必要条件是工作流自身必须是基于状态的工作流而不是

顺序工作流。然后确定你的状态活动应容纳些什么子活动,并按需要把它们拖拽进去,牢记

你只能插入四种类型的活动。

使用SetState活动

假如你回忆一下我在第四章中介绍过的状态机示例的话,下图14-1 将看起来很眼熟。

确实,它是一个(被简化了的)自动售货机状态图。我认 为把这样一个状态图制成一个真实

的基于状态的WF 工作流并使用一个用户界面来驱动它会是很有趣的一件事,考虑到我缺少艺

术细胞,该用户界面会被构成成一个 简陋的不含酒精饮料(“汽水”)的自动售货机。

image.png

图14-1 饮料机的状态图

考虑到没有用户互动,该饮料售货机应用程序的界面如图14-2 所示。一瓶饮料的价格

是$1.25。当你投入硬币的时候,左边的饮料图 形按钮都处于非激活状态。但是,当你投入

了足够的金额后,这些饮料按钮就能使用并且你可以做出选择。这个简化的模型不会处理如

退款和更改之类的事情,但如 果你愿意的话,你可随意修改该应用程序。

备注:为简便起见,我并没有使该示例应用程序国际化。它模拟的是只接受美国货币的

自动售货机。但是,请记住这里的重点是工作流,而不是所使用的货币单位。

image.png

图14-2 饮料机处于初始状态时的用户界面

但是,你不能真正把硬币投到一个Windows Forms 应用程序中,因此我提供了5¢,10¢

和25¢三个按钮(注:符号¢代表美分)。很抱歉,只有这几种硬币。当你首次点击其中一

个硬币按钮的时 候,一个新的基于状态的工作流实例就被启动了,执行该工作流的状态图如

图14-1 所示。图14-3 为你展示了饮料机在投入了几个硬币后的情况。基于状态的 工作流随

时跟踪接收到的硬币并把金额总计反馈给应用程序,该应用程序在一个模拟的液晶二极管显

示屏上把它显示出来。

image.png

图14-3 投入了硬币的饮料机用户界面

当投入了足够的硬币时,工作流就通知应用程序现在用户可以选择饮料了,如图14-4

所示。应用程序让位于用户界面左边的各个饮料按钮处于可用状态(enable)。

image.png

图14-4 允许选择饮料的饮料机用户界面

当点击了左边的某个饮料按钮后,即如图14-4 中显示的变黑的按钮,一个标签(label)

将呈现出来并显示“Soda!”,这是我模 拟一瓶客户选中的饮料从机器中落出的一种方式。

为重置整个过程,可点击“Reset”按钮。这不会影响到该工作流但是会重置用户界面上的按

钮。图14-2 显示了这种情形的用户界面,你可再一次启动所有的处理过程。

image.png

图14-5 选中了某瓶饮料后的饮料机用户界面

已经为你创建了大量的应用程序代码。假如你读完该SodaMachine 示例的代码,你将

发现我使用了CallExternalMethod 活动(看看第8 章中的“工作流数据传送”)以及

HandleExternalEvent 活动(看看第10 章“事件活动”)。有大量的工具来为你的工作流和

你的应用程序之间进行交互。剩下的工作就是创建该工作流自身,下面就是具体的做法。

创建一个基于状态的工作流

1.该SodaMachine 应用程序再次为你提供了两个版本:完整版本和非完整版本。你需要

下载本章源代码,打开SodaMachine 文件夹中的解决方案。

2.当SodaMachine 解决方案在Visual Studio 中打开后,从Visual Studio 的“生成”

菜单中选择“生成解决方案”。解决方案中的项目包含各种各样的依赖,编译该解决方案生

成了那些关联的项目所能引用的程序集。

3.在Visual Studio 的解决方案资源管理器窗口中找到SodaFlow 项目中的Workflow1.cs

文件。然后在工作流视图设计器中打开该工作流准备编辑。

备 注:我已经创建了这个基本的工作流项目,因为该应用使用的CallExternalMethod

和HandleExternalEvent 活动的相关技术 你在第8 章和第10 章中已经看过。重复这些必须

的步骤来创建这些常规活动没有任何必要,但是假如你从头开始创建工作流项目的话,你需

要去做这些工作。

image.png

4.该工作流现由唯一的一个State 活动组成。选中该stateActivity1 活动,把它重命名

为“StartState”。

5.当创建工作流时,Visual Studio 会为你自动添加一个原始的State 活动。但它也把

这个活动作为起始(开始)活动。当你在前一步骤中重命名该活动后,工作流就会丢失这个

行为。为把这个活动重新设置为起始活动,需要在工作流视图设计器的界面中点击除了State

活动之外的其它任何地方,以便激活整个工作流的属性。在属性面板中,你会看到一个

InitialStateName 属性,把它从stateActivity1 改为StartState。注意你可把这个值直接

输入到该属性中也可从下拉列表框中选择StartState。

image.png

6.我们现在要把剩下的State 活动拖拽到工作流视图设计器的界面上。和你记得的一样,

当SetState 工作的时候,可方便地指定目标状态。从Visual Studio 的工具箱中拖拽一个

State 活动到设计器的界面上并把它放到StartState 活动的旁边。把它的名称改为

image.png

7.拖拽另一个State 活动到工作流视图设计器的界面上,把它的名称改为

WaitSelectionState。

image.png

8.拖拽最后的一个State 活动到工作流视图设计器的界面上,把它的名称改为EndState。

image.png

9.就像你要重新指定开始状态一样,你也需要告知WF 结束状态是什么。点击任何State

活动外面的工作流视图设计器界面来激活该工作流的属性。指定CompletedStateName 属性为

EndState。然后Visual Studio 会清空EndState 的内容并改变它左上角的图标。和前面一样,

你可直接输入EndState 也可从下拉列表框中选择它。

image.png

10.放好了这些状态活动,我们现在就来添加些细节。从StartState 开始,从工具箱中

拖拽一个StateInitialization 活动并把它放到StartState 中。

image.png


11.双击你刚刚添加的这个stateInitialization1 活动,这将进入到顺序工作流编辑器

中。

image.png

12.从工具箱中拖拽一个Code 活动到该StateInitialization 活动中。指定它的

ExecuteCode 方法为ResetTotal。然后Visual Studio 会为你添加对应的ResetTotal 方法并

为你切换到代码编辑视图下。此时我们不准备添加代码,还是回到工作流视图设计器上来吧。

image.png

13.接下来拖拽一个SetState 活动到设计器界面上,把它放到你刚刚添加的Code 活动的

下面。

image.png

14.指定该SetState 的TargetStateName 属性为WaitCoinsState。


image.png

15.回到工作流视图设计器的状态编辑器视图中,点击Workflow1 左上角的超链接风格的

按钮。

image.png

状态编辑器现在会显示出StartState 向WaitCoinsState 的转变。

image.png

16.StartState 现在就完成了。下一步我们将转到WaitCoinsState。首先拖拽一个

EventDriven 活动到设计器的界面上并把它放到WaitCoinsState 中。在Visual Studio 的属

性面板中把它的Name 属性修改为CoinInserted。

image.png

17.双击CoinInserted EventDriven 活动使顺序工作流编辑器呈现出来。

image.png

18.现在从工具箱中拖拽一个CoinInserted 自定义活动到该EventDriven 活动的表面上。

注意,假如你还没有编译整个解决方案的话,该CoinInserted 事件是不会在工具箱中显示出

来的。假如你漏过了第2 步,你可能必须移除该EventDriven 活动以便成功地进行编译。

image.png

19.在工作流视图设计器中选中该ExternalEventHandler coinInserted1 活动,在属性

面板中点击CoinValue 属性以便激活浏览(...)按钮,然后点击该浏览按钮。这将打开“将

‘CoinValue’绑定到活动的属性”对话框。点击“绑定到新成员”选项卡,在“新成员名

称”中输入LastCoinDropped。此时选中的应该是“创建属性”,假如不是的话选中它,以

便你创建的是一个新的依赖属性。然后点击“确定”。

image.png

20.现在我们需要做一个判断:用户刚刚投入了足够的金钱来使那些饮料按钮处于可用

(enable)状态吗?为此,拖拽一个IfElse 活动到工作流视图设计器界面上,把它放到

CoinInserted EventDriven 活动中,它的位置在coinInserted1 的下面。

image.png

21.选中ifElseActivity1 的左边分支,以便在属性面板中显示它的属性。对于它的

Conditon 属性,选择“代码条件”。然后展开Condition 节点,然后在Condition 子属性中

输入TestTotal。在Visual Studio 添加一个新的方法并为你切换到代码编辑视图后,重新

返回到工作流视图设计器上来。

image.png

22.TestTotal 将检测你最终投入到饮料机中的金额总计。(我们将在添加代码前完成该

工作流在工作流视图设计器中的设计工作,因为有一些我们需要的属性还没有创建。)假如

投入了足够的金钱,我们就需要转换到WaitSelectionState。因此,拖拽一个SetState 到

该IfElse 活动(ifElseBranchActivity1)的左边分支上,指定它的TargetStateName 为

WaitSelectionState。

image.png

23.假如TestTotal 判定了没有足够的金额来买饮料,该工作流需要传达当前投入到饮料

机中的钱的总计。为此,从工具箱中拖拽一个UpdateTotal 并把它放到该IfElse 活动的右边

分支中。UpdateTotal 是我为本任务创建的一个自定义的CallExternalMethod 活动。

image.png

24.UpdateTotal 需要一个要去通信的总计值,因此选中它的total 属性并点击浏览(...)

按钮,这将再次打开一个绑定对话框。当绑定对话框打开后,选择“绑定到新成员”选项卡

并在“新成员名称”中输入Total 并确认选中的是“创建属性”选项。然后点击“确定”。

image.png

25.点击左上角的超文本风格的Workflow1 按钮回到状态设计器视图。拖拽一个

StateFinalization 到工作流视图设计器的界面上,把它放到WaitCoinsState 中。

image.png

26.双击你刚刚添加的stateFinalizationActivity1 活动重新激活顺序设计器视图。

image.png

27。从工具箱中拖拽一个ReadyToDispense 并把它放到stateFinalizationActivity1 中。

ReadyToDispense 也是一个自定义的CallExternalMethod 活动。

image.png

28.你刚刚添加的ReadyToDispense1 活动将把最终的总计值返回给主应用程序。为做这

些,它需要访问你在第14 步中添加的Total 属性。看看readyToDispense1 的属性,点击

finalTotal 属性,然后点击在finalTotal 中激活的浏览(...)按钮。点击浏览按钮打开绑

定对话框,但是这次是“绑定到现有成员”。从列表中选择Total 属性然后点击“确定”。

image.png

29.点击超文本风格的Workflow1 按钮回到状态设计器视图上来。这里,从工具箱中选择

EventDriven 活动并把它放到设计器界面上的WaitSelectionState 活动中。把它命名为

ItemSelected。

image.png

30.双击ItemSelected EventDriven 活动进入顺序设计器视图。

image.png

31.拖拽一个自定义ExternalEventHandler 的活动ItemSelected,把它放进

ItemSelected EventDriven 活动中。

image.png

32.用户挑选了饮料后,主应用程序触发该ItemSelected 事件。当该事件发生的时候,

我们需要切换到EndState。为此,我们需要添加SetState 活动。因此从工具箱中拖拽一个

SetState 并把它放到ItemSelected EventDriven 活动中的itemSelected1 的下面。指定它

的TargetStateName 为EndState。

image.png

33.点击超文本风格按钮Workflow1 回到状态设计器视图上来。

image.png

34.从工作流视图设计器的角度来看,该工作流就完成了,但我们还要写一些代码。在

Visual Studio 的解决方案管理器中选择Workflow1.cs 文件,然后在代码编辑模式下打开该

文件准备进行编辑。

35.查看Workflow1.cs 源文件,找到你在第12 步所添加的ResetTotal 方法。把下面的

代码插入到ResetTotal 方法中:

// Start with no total.

Total = 0.0m;

36.最后,找到你在第21 步所添加的TestTotal 方法。为该方法添加下面这些代码:

// Add the last coin dropped to the total and check

// to see if the total exceeds 1.25.

Total += LastCoinDropped;

e.Result = Total >= 1.25m;

37.编译整个解决方案。修正任何可能出现的编译错误。

现在你可按下F5 或Ctrl+F5 运行该应用程序。点击一个投币按钮,LCD 上显示的总金额更新

了吗?当你投入了足够的金钱时,你能挑选饮料吗?

备注:假如该应用程序由于InvalidOperationException 异常崩溃的话,最可能的情况

是由于引用在解决方案第一次完成编译后没有被完全更新。可简单地重新编译整个应用程序

(重复第37 步)并再次运行该应用程序,它应该能干净利落地运行。

源码下载 http://files.cnblogs.com/gyche/WF%20Step%20by%20Step/Chapter14.rar



--------------------- 

作者:hackpig

来源:www.skcircle.com

版权声明:本文为博主原创文章,转载请附上博文链接!


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年3月    »
123
45678910
11121314151617
18192021222324
25262728293031
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864