一、工作流概述
工作流是由活动单元组成的集合,活动是真实过程的的一个模型。工作流提供了一种描述一系列相互关联的工作之间有执行顺序,这种工作从头到尾贯穿了整个活动,这些活动可能是由人工或系统来执行。
每一个运行的工作流实例由工作流运行时引擎来创建和维护的。虽然对于每一个应用程序域只能有一个工作流运行时引擎,但工作流运行时引擎内可以行多个工作流实例并发工作。
一旦一个工作流模型被编译,它就可以在任何一Windows进程内工作,包括控制台程序,窗口程序,Windows服务程序,Asp.net网站及Web Service等。因为工作流驻留在进程中,所以它可以很容易与它的宿主进程进行通信。
下面这幅图表明了工作流、活动以及工作流运行时引擎都存在于一个宿主程序中。
活动
如上所述,活动是工作流的基本单元,它们通过程序被加入到一个工作流中,其方式就好比将一个XML DOM子节点加入到根节点中。一旦工作流中的所有节点都运行完成,工作流实例就会结束。
WF由一系列标准活动类库组成,同时也提供了一个机制帮助开发人员开发自己的类库。这使得工作流之间的可扩展性和可重用性更加优异。
服务
当一个工作流运行的时候,工作流运行时引擎要使用到多个服务。这些服务组件是可插拔的,这使得应用程序可以在它们的运行环境中,提供具有唯一性的服务。Windows Workflow Foundation提供了这些运行时服务的默认实现,可以满许多种应用程序需求。例如,它提供的这些组件实现了工作流实例的调度模型,工作流及其宿主程序之间的通信,怎样监视和跟踪你的工作流等等。
开发人员同样可以从平台提供的服务基类继承,来自定义工作流,从而扩展WF平台。
有状态的、稳固的、长时间运行的应用程序
WF简化了编写有状态的、稳固的、长时间运行的程序的过程。工作流运行时引擎管理着工作注的执行,并允许工作流长期保持活动状态,哪怕是计算机重新启动。运行时服务通过完美的错误管理机制,来实现这一点。
WF包含一个标准的服务,它与Microsoft SQL Server 2005 Express, Microsoft SQL Server 2000 or later or Microsoft SQL Server 2000 Desktop Engine (MSDE) 很好的集成,从而能列容易和高效的保证工作流的稳固性。
关于Visual Studio 2005 Extensions
使用WF组件来开如工作流程序,不一定要用Visual Studio 2005,但是,Visual Studio 2005 Extensions for Windows Workflow Foundation包含了许多工作流和自定义活动的设计器,将会使得开发工作更加方便。
二、使用WF开发介绍
WF允许我们用.net framework来开发自己的工作流程序,它并不是一个可执行程序,但它使得我们能开发自己的工作流应用程序。
传统的通用编程语言致力于开发短生命期的程序,它们缺乏对程序运行的持续性和稳固性的内在的支持。WF是一个框架,不是一门语言。它对工作流编程有着深入的理解,从而能很好的处理诸于persistence and compensation这样的工作。
WF是灵活的,可扩展的。你可以直接通过代码或markup(?)来编写工作流,可使用两者组合。你可以通过定义可重用的活动来,来实现自定义的工作流模式。
WF支持模型驱动的工作流开发,它支持自然的可视化设计,同时隐藏了一些系统级的概念,如事务、状态管理以及并发控制。
开发工作流程序有两件基本的事情,一是定义工作流和它的活动,再就是在程序使用这些工作流。
(一)工作流程模式
WF支持多种工作流程式,如顺序工作流,以及数据驱动。顺序模式是一直向前执行,它在重复和、可预见的操作(这些操作通常相同)中非常有用。状态机模式由一系列事件驱动的状态组成,数据驱动模式依赖于数据来决定某个活动是否执行。
顺序工作流
顺序模式是一直向前执行,它在重复和、可预见的操作(这些操作通常相同)中非常有用,例如,一系列的活动以规定的、通常相同的顺序来运行。参见下图。
顺序工作流以按顺序执行的方式来执行活动,直到最后一个活动执行完成。顺序工作流不一定是完全确定的,即使在非正常操作中。例如,你可以使用一个“监听”活动或一个“并发”活动,事件的具体顺序在不同的情况下可以不同。
状态机工作流
在状态机模型中,工作流就是一个状态机,它由一系列的状态组成。其中一个状态指示了开始状态,每个状态都可以接收一系列的事件。基于一件,工作流从一个状态迁移到另一个状态。状态机工作流可以有一个终结状态,当迁移到终结状态时,工作流就完成了。
下面这幅图是一个状态机工作流的例子。
下面这张表列出了在WF框架的活动集合中与状态机相关的活动
活动 | 说明 |
EventDrivenActivity | 用于依赖于外部事件才能开始执行的状态。EventDrivenActivity必须有一个Activity实现在了IEventActivity 接口,作为其第一个子活动。 |
SetStateActivity | 用于迁移到一个新状态。 |
StateActivity | 代表状态机中的一个状态,可以包含附加的“状态”活动 |
StateInitializationActivity | 当进一个状态时执行,可以包含其它活动。 |
StateFinalizationActivity | 当离开一个StateActivity时会执行它所包含的活动。 |
使用活动来控制流程
WF框架包含不同功能的活动,我们可以这些活动来控制工作流的逻辑流程。
下面这张表列出了在WF框架的活动集合中用来控制工作流逻辑流程的活动。
活动 | 说明 |
ListenActivity | 可以根据一些事件或超时操作来产生分支。 |
IfElseActivity | 在每个分支上测试同一个条件,并且执行第一个返回True的分支上的活动。 |
ParallelActivity | 可以使工作流同时执行两个以上互不相关的操作。 |
SuspendActivity | 挂起一个操作,可以产生错误用来干预流程。 |
TerminateActivity | 当错误发生时,可以用来立即终止工作流中的一个操作。 |
WhileActivity | 循环执行一个操作,执行指定的条件得到满足。 |
ConditionedActivityGroup | 也被称为CAG。当设定到CAG的条件满足时,会执行CAG包含的子活动。子活的执行也必须满足该子活动设定的条件。 |
EventDrivenActivity | 它是一个包含另一个活动的活动,在指定的事件发生时执行。 |
在工作流中使用条件
我们可以通过条件来控制工作流的状态。运行时引擎会自动判别一个条件,并根据判别的结果来执行某一些操作。
有两种方式来表达工作流中的条件:
一是代码方式,编写一个Handler返回Bool值;(注:以下译作代码条件)
二是通过工作流定义中的规则(rule)。(注:以下译作规则条件)
我们可以在运行的时候动态的更新规则条件来改变工作流的行为。
我们还可以使用PolicyActivity,通过规则来完成有条件的行为。
条件活动
WF提供了多个使用条件的活动:
- IfElseActivity分支:基于对条件结果的判别。
- WhileActivity:只要条件保持为True,就会持续的执行它包含的所有活动。条件的值会在每次循环执行时重新进行判别。
- ConditionedActivityGroup:持续的执行它包含的所有活动直到其条件为真,它包含的每个活动都有一个When条件,只有当When条件为真时,活动才会执行。
你可以在你创建的工作流中自定义条件。
注意:规则条件可以引用public成员(即在你的工作流类中定义的变量或方法),而代码条件可以引用public或private成员。
使用规则条件
规则条件在条件表达式编辑器中定义,根据工作流状态和数据来确定条件的值。运行时引擎会判别这个条件,从而决定要不要让与之相联的活动执行或使之可用。
规则条件非常有用,因为你在运行的时候可以修改它们,这意味着你可以不用停止、修改或编译你的工作流就可以改变它的行为。
可以使用下面的要素来构建规则条件:
- 逻辑操作符,如AND,,OR, NOT
- 算术操作符,如+,-,*,/
- 工作流中的public成员,间接地使用其它程序集中的成员
对包含复杂类型的变量的嵌套调用,这种变量如:this.Address.State == "WA"
注意:if关键字会被自动添加到条件语句的前面,但它不会显示在这个语句中。
动态更新规则条件
你可以通过动态更新规则条件,在工作流运行的时候修改它的行为,而不需要重新编译。
条件会作为活动的一个属性出现,这些活动诸如: WhileActivity, IfElseActivity, ReplicatorActivity, 和 ConditionedActivityGroup。宿主程序会显式的更新这些条件定义,更新后的条件会在下次条件被判别时使用到。
因为条件是可序列化的,如果工作注被暂停或终止时,更新后的条件也会被保存。
重要:任何动态更新只能当前正在运行的实例有效,而不会影响到工新创建的作流实例。
使用代码条件
要以用C#或VB.net来创建条件。代码条件简单看,是分隔代码的处理器,它为ConditionalEventArgs.Result设置一个布尔值。代码条件被编译后,成为程序集一的部份。运行时引擎执行这个方法,并返回一个一个布尔值作为代码条件的判别结果。
下面的例子是一个用于判别某个值是否大于10的一个代在条件:
{
e.Result = ( this .CustomPropertyValue > 10 );
}
WF支持的CodeDOM类型
WF System.CodeDOM。你可使用这些类型来创建你的表达式,其中一些用于条件(conditions),一些用于规则(actions),还有一些能用于两者。
注意:CodeDOM不支持一元操作符,也不支持不等操作符,如a!=b,你可以换成这样表达:(a==b)==false
下表列出了WF所支持的CodeDOM类型。
类 | 用于 |
CodeAssignStatement | Actions |
CodeBinaryOperatorExpression | Conditions |
CodeDirectionExpression | Conditions |
CodeExpressionStatement | Actions |
CodeFieldReferenceExpression | Conditions |
CodeMethodInvokeExpression | Conditions |
CodeMethodReferenceExpression | Conditions |
CodePrimitiveExpression | Conditions |
CodePropertyReferenceExpression | Conditions |
CodeThisReferenceExpression | Conditions |
CodeTypeReference | As part of expressions |
CodeTypeReferenceExpression | Conditions |
CodeBinaryOperatorType | Supported in |
Add | Conditions, Actions |
BitwiseAnd | Conditions, Actions |
BitwiseOr | Conditions, Actions |
BooleanAnd | Conditions, Actions |
BooleanOr | Conditions, Actions |
Divide | Conditions, Actions |
GreaterThan | Conditions, Actions |
GreaterThanOrEqual | Conditions, Actions |
IdentityEquality | Conditions, Actions |
IdentityInequality | Conditions, Actions |
LessThan | Conditions, Actions |
LessThanOrEqual | Conditions, Actions |
Modulus | Conditions, Actions |
Multiply | Conditions, Actions |
Subtract | Conditions, Actions |
ValueEquality | Conditions, Actions |
在工作流中使用事务
WF提供的TransactionScopeActivity封装了.NET System.Transactions的事务功能,可以在操作发生错误时自动回滚,从而支持从事务失败中恢复。请参考MSDN中获取System.Transactions.Transaction和System.Transactions.TransactionScope类的更详细信息。
工作流中的批处理状态信息
工作流会周期性在各个检查点保存它有状态,如果发生错误,工作流引擎有必要检索保存过的信息来返回到一个稳定的状态。如果两个以上的组件正在通信,那么组件间的coordinate persistence有助于保证组件的一致性。
这一点在发送或接收消息时尤其有用。例如,一个工作流可能发送多条消息,每个发送的操作都会请求消息服务。消息服务必须要维护工作流的一致性和持续的状态,只有工作流状态被成功保存,消息才能被发送。这意味着在单个的事务中,工作流状态以及消息发送状态都会被保存起来,以保存所有组件间状态的一致性。
WF提供了 System.Workflow.Runtime.IWorkBatch 和 System.Workflow.Runtime.IpendingWork两个接口来解决这个问题。
在对服务所有调用中,运行时引擎在自己的线程上下文中提供了System.Workflow.Runtime.IworkBatch,你可以添加一个挂起的工作项到批处理中,这样运行时引擎可以一次性将所有相关的工作项提交到事务处理。
在ExternalDataEventArgs的构造函数中,可以传递一个IpendingWork类型的参数。
当组件被请求时的执行顺序
1、在第一次请求前,工作流创建工作批处理对象。
2、工作流将工作批处理传对象递到到组件的请求方法中。
3、组件创建一个工作项,并将其添加到工作批处理对象中。
其它组件请求时,重复第二步和第三步。
在事务提交点的执行顺序
1、工作流创建一个事务
2、工作流遍历工作批处理对象中的工作项,并收集其中属于某一组件的工作项,维护其顺序,然后创建一个新工作批处理对象。工作流调用组件的Commit方法,并把事务对象和这个批处理对象传递给它。
3、组件把工作批处理对象中的工作添加到事务中。
对所有的组件重复第二步和第二步。
当组件的Commnit方法成功调有后,工作注提交相应的事务。
当事务成功提交后,工作流遍历工作批处理中工作项,如第二步一样。工作流调用每个组件的Complete方法,交把事务对象和批处理对象传递给它。
工作流发生错误时的执行顺序
1、工作流识别错误范围内的所有工作项并构造一个工作批处理对象,并将这些工作项添加这个工作批处理对象中。对于2、工作批处理中所有工作,工作流通过IPendingWork接口来调用其Complete方法,并传递完成状态为false
3、工作流取消工作批处理中的所有工作。
从错误中恢复后,运行时维护剩余的工作批处理项引用。
转自:http://www.cnblogs.com/watsonyin/
工作流发生错误时的执行顺序
1、工作流识别错误范围内的所有工作项并构造一个工作批处理对象,并将这些工作项添加这个工作批处理对象中。对于2、工作批处理中所有工作,工作流通过IPendingWork接口来调用其Complete方法,并传递完成状态为false
3、工作流取消工作批处理中的所有工作。
从错误中恢复后,运行时维护剩余的工作批处理项引用。
WF中的错误处理
WF中的错误是以异步的方式来处理的。在活动中显式或隐式的抛出异常后,工作流运行时引擎会将捕捉到的异常先保存到队列中,在之后的时间再进行处理。这与一般的异常处理方式不同在于,如果一个异常在try{}语句块中被抛出,在相应的catch{}语句块中无法截获该错误,同时该错误也不会立即抛出给用户。
异常的起因
以下几种情况可能会产生异常
Ø 原子事务超时
Ø 其它类型的事务失败
Ø 宿主程序通过ThrowActivity抛出异常
Ø 用户代码错误。当工作流调用外部的用户代码时,CLR类可能会抛出异常,如果这种异常在用户代码中没有被处理,它们最终会以工作流异常的的表式出现。
Ø 其它类型的系统异常,例如保存失败,.net或系统异常,或数据转换错误等
错误捕获
在错误处理中,如果抛出异常的活动不能处理它,异常会被转交到它的上一级活动处理,直到它被处理为止,否则的话,工作流实例就会被工作流运行时引擎终止。
错误处理是由FaultHandlerActivity活动来完成的。每个FaultHandlerActivity活动都与一个.net异常类型相关联,并且它包含一系列的活动,如果错误类型与某个FaultHandlerActivity关联的错误类型匹配的话,就会执行为个FaultHandlerActivity活动。一个FaultHandlersActivity活动包含0-n个FaultHandlersActivity子活动,FaultHandlersActivity可以是任何复合活动的子活动。
WF中的错误处理通常被视为一个反向的工作,因为它的目标是当异常发生时,撤销活动中不成功的部分的工作。FaultHandlerActivity的执行完成不能被认为是事务活动的成功完成。事务活动会先被设置成错误状态,然后是关闭状态。任何兄弟活动同样会被取消。同时,compensation也会成为不可用状态。
在工作流中使用Compensation(下面译为补偿)
WF中对于已完成事务的补偿模式,是对工作流中出现的异常进行处理的过程,并且会在逻辑上撤销已完成的事务。
WF补偿有以下两种形式:
1、当异常没有被处理的时候,系统隐式的补偿;
2、 使用Compensate活动进行显式的补偿。
WF中的错误处理
WF中的错误是以异步的方式来处理的。在活动中显式或隐式的抛出异常后,工作流运行时引擎会将捕捉到的异常先保存到队列中,在之后的时间再进行处理。这与一般的异常处理方式不同在于,如果一个异常在try{}语句块中被抛出,在相应的catch{}语句块中无法截获该错误,同时该错误也不会立即抛出给用户。
异常的起因
以下几种情况可能会产生异常
- 原子事务超时
- 其它类型的事务失败
- 宿主程序通过ThrowActivity抛出异常
- 用户代码错误。当工作流调用外部的用户代码时,CLR类可能会抛出异常,如果这种异常在用户代码中没有被处理,它们最终会以工作流异常的的表式出现。
- 其它类型的系统异常,例如保存失败,.net或系统异常,或数据转换错误等
异常捕获
错误处理中,如果抛出异常的活动不能处理它,异常会被转交到它的上一级活动处理,直到它被处理为止,否则的话,工作流实例就会被工作流运行时引擎终止。
错误处理是由FaultHandlerActivity活动来完成的。每个FaultHandlerActivity活动都与一个.net异常类型相关联,并且它包含一系列的活动,如果错误类型与某个FaultHandlerActivity关联的错误类型匹配的话,就会执行为个FaultHandlerActivity活动。一个FaultHandlersActivity活动包含0-n个FaultHandlersActivity子活动,FaultHandlersActivity可以是任何复合活动的子活动。
WF中的错误处理通常被视为一个反向的工作,因为它的目标是当异常发生时,撤销活动中不成功的部分的工作。FaultHandlerActivity的执行完成不能被认为是事务活动的成功完成。事务活动会先被设置成错误状态,然后是关闭状态。任何兄弟活动同样会被取消。同时,compensation也会成为不可用状态。
在工作流中使用Compensation(下面译为补偿)
WF中对于已完成事务的补偿模式,是对工作流中出现的异常进行处理的过程,并且会在逻辑上撤销已完成的事务。
WF补偿有以下两种形式:
当异常没有被处理的时候,系统隐式的补偿;
使用Compensate活动进行显式的补偿。
在工作流中使用动态更新
动态更新使得我们可以在工作流实例运行的时候对它做出改变。做这些改变,可能是由于设计时候的疏忽,或者是属性的改变,活动的绑定,或者是业务逻辑得到了加强和完善。如果你需要整个改变工作流,动态更新并不适用,否则会导致工作流与最初的设计原则完全不同。在这种情况下,你应该设计一个新的工作流。
动态更新适用于工作流的单个实例。如果已经有工作流实例运行,不能对工作流中的进行类型的改变。
你可以使用动态更新来干面的事情:
- 改变正在运行的工作流实例;
- 改变运行时行为;
- 改变工作流结构,例如,添加或移除一个活动;
- 改变流程控制;
- 为对一个已经存在的活动定义一个新的条件;
- 改变一个PolicyActivity的RuleSet;
- 如果在工作流在已经部署和运行后, 需要在工作流中增加一个新的业务过程,可以通过添加一个新的自定义活动,或InvokeWorkflowActivity活动来实现;
- 添加一个新的EventDrivenActivity来响应一个新的事件,如一个附加的确认步骤;
对一个正在运行的工作流进行动态更新,可能会导致两个不同的入口点:一个在工作流代码文件内,一个在工作流外,如工作流宿主程序。
工作流中的数据交换
WF通信服务支持在工作流服务环境内的数据交换,它使得工作流可以以方法和事件的形式,与外部系统通过消息进行通信。
WF通过web serivce通信来实现工作流之间的数据交换。
在工作流中使用角色
WF提供了一种基于角色的访问机制,适用于所有支持数据输入的活动。工作流的创建者有完全的控制权限来创建角色和角色集合。创建者通过这种方式,可以提供一种必要的权限机制,在活动被执行之前,来检测访问者角色的权限。
有一些WF活动,如WebServiceInputActivity,通过其属性就可以取得或设置角色集合。
使用活动目录(Active Directory,),可以使得工作流的开发更加容易,WF通过一种被称为out-of-the-box的机制,来创建基于活动目录的角色。请参考MSDN,了解更多关于.net framework和活动目录的安全机制。
与角色相关的类有WebWorkflowRole,WorkflowRole,ActiveDirectoryRole等等,在System.Workflow.Activities命名空间中。
编译工作流
工作流可以通过以下三种方式被创建:
通过工作流markup文件,使用工作流markup,该文件的后缀为.xoml。通过宿主程序,把该文件传入给工作流运行时引擎,这种方式被称为“无代码工作流创建”,这种方式下,工作流不需要编译。被创建的文件名WorkflowSample.xoml(举例)
另一种方式是,markup文件用来声明工作流,然后与包含逻辑实现的代码一起被编译。这种方式被称为“代码分离工作流创建”。被创建的文件名为:WorkflowSample.xoml 和WorkflowSample.xoml.cs
在代码文件中使用WF对象模型。这种方式称为“仅代码工作流创建”。被创建的文件名为:WorkflowSample.cs 和 WorkflowSample.Designer.cs,这些文件会被编译。
当工作流被编译时,会执行以下过程:
- 验证工作流活动是否符合为该活动设置的规则,如果验证产生错误,编译器会返回一个错误列表;
- 据传入到编译器中的标记(markup)定义,创建一个partial类;
- 从标记文件产生的partial类和人代码文件产生的partial类,被送入到.net编译器,这个过程的输出结果就是一个.net 程序集。文件名如:WorkflowSample.dll,可以被部署运行工作流了。
在命令行中使用wfc.exe
WF提供了命令行方式的工作流编译器,即wfc.exe。WF同样提供一系列的支持工作流编译公共类型,可以用来开发自定义的工作流编译器。这些公共类型也是wfc.exe所用来的类型。你可以通过WorkflowCompiler类来创建一个自定义的编译器。
你还可以使用WorkflowCompiler类来编译工作流,例如:
WorkflowCompilerParameters param = new WorkflowCompilerParameters();
compiler.Compile(param, new string [] { " MainWorkflow.xoml " });
wfc.exe编译选项
wfc命令行的选项如下
Microsoft (R) Windows Workflow Compiler version 3.0.0.0
Copyright (C) Microsoft Corporation 2005. All rights reserved.
Windows Workflow Compiler Options
wfc.exe <XAML file> /target:codegen [/language:...]
wfc.exe <XAML file list> /target:assembly [<vb/cs file list>] [/language:...]
[/out:...] [/reference:...] [/library:...] [/debug:...] [/nocode:...] [/checktypes:...]
- OUTPUT FILE -
/out:<file> Output file name
/target:assembly Build a Windows Workflow assembly (default).
Short form: /t:assembly
/target:exe Build a Windows Workflow application.
Short form: /t:exe
/target:codegen Generate partial class definition.
Short form: /t:codegen
/delaysign[+|-] Delay-sign the assembly using only the public portion
of the strong name key.
/keyfile:<file> Specifies a strong name key file.
/keycontainer:<string> Specifies a strong name key container.
- INPUT FILES -
<XAML file list> XAML source file name(s).
<vb/cs file list> Code file name(s).
/reference:<file list> Reference metadata from the specified assembly file(s).
Short form is '/r:'.
/library:<path list> Set of directories where to lookup for the references.
Short form is '/lib:'.
- CODE GENERATION -
/debug[+|-] Emit full debugging information. The default is '+'.
Short form is '/d:'.
/nocode[+|-] Disallow code-separation and code-within models.
The default is '-'. Short form is '/nc:'.
/checktypes[+|-] Check for permitted types in wfc.exe.config file.
The default is '-'. Short form is '/ct:'.
- LANGUAGE -
/language:[cs|vb] The language to use for the generated class.
The default is 'CS' (C#). Short form is '/l:'.
/rootnamespace:<string> Specifies the root Namespace for all type declarations.
Valid only for 'VB' (Visual Basic) language.
Short form is '/rns:'.
- MISCELLANEOUS -
/help Display this usage message. Short form is '/?'.
/nologo Suppress compiler copyright message. Short form is '/n'.
/nowarn Ignore compiler warnings. Short form is '/w'.