博客园  :: 首页  :: 联系 :: 订阅 订阅  :: 管理

坚持学习WF(7):流程控制(Flow Control)

Posted on 2008-04-30 18:10  生鱼片  阅读(6326)  评论(6编辑  收藏  举报

本文主要说说WF中和流程相关的活动,主要包括以下这些活动:IfElseActivity,WhileActivity,ParallelActivity,ReplicatorActivity,ConditionedActivityGroup,InvokeWorkflowActivity,TerminateActivity,SuspendActivity等。

这些和流程相关的活动大多和条件相关,WF中的条件有两种形式代码条件和声明性规则条件,它们都是继承自ActivityCondition。

代码条件:通过工作流中的一个方法来实现的,该方法通过ConditionalEventArgs的Result数据返回true或false,是System.Workflow.Activities.CodeCondition类的实例。
声明性规则条件:被序列化到.Rules的Xml文中,这些都是简单的使用条件,复杂的情况我们可以使用RuleSet来实现。

ParallelActivity活动

IfElseActivity,WhileActivity比较简单,就不说了,先来说说ParallelActivity活动,它可以让多个分支活动同时执行,所有的分支必须都执行完才可以执行下一个活动。SequenceActivity活动是唯一可以作为ParallelActivity的子活动,只有所有的子活动完成后,ParallelActivity才会完成,虽然 SequenceActivity 子分支同时得到处理,但分支活动不会真正同时执行。 WF 运行时对每个工作流实例使用一个线程。 ParallelActivity 活动中所有单独的分支共享同一个工作流实例线程。 因此,一次只执行一个分支的一个活动。这就可以应用到我们实际的场景中,比如你有一份财务申请单,在你的某一个流程点你希望财务主管和部门主管同时核可才可以生效,流入下一流程,你就可以使用该活动来实现。

根据测试,如果ParallelActivity中有多个分支,每个分支又有多个活动,那么他会先执行第一个分支的第一个活动,然后在执行第二个分支的第一个活动,依此下去,当所有分支的第一个活动都执行完了,就会又回到第一个分支来执行第一个分支的第二个活动,如下图所示:

01

当一个或多个分支中有一个或多个阻止活动时(如DelayActivity),ParallelActivity尤其有用。这种情况下可以改变分支的执行顺序,如下图所示:

02

Replicator活动

Replicator活动和C#中的foreach语句有些相似,可以在运行时创建单个活动的任意多个实例。 每个 ReplicatorActivity 活动都只能包含一个子活动,但该子活动可以是复合活动。我们先来看一个例子,这个例子很简单,我们建一个顺序型的工作流,然后拖一个Replicator活动,在该活动中加入一个CodeActivity,代码如下:

using System;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.Workflow.Activities;
using System.Collections.Generic;

namespace CaryWorkflows
{
    public sealed partial class ReplicatorWorkflow : SequentialWorkflowActivity
    {
        public static DependencyProperty InputListProperty
            = System.Workflow.ComponentModel.DependencyProperty.Register(
                "InputList", typeof(List<String>), typeof(ReplicatorWorkflow));
        [Description("A list of strings to process")]
        [Category("Flow Control")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public List<String> InputList
        {
            get
            {
                return ((List<String>)(base.GetValue(ReplicatorWorkflow.InputListProperty)));
            }
            set
            {
                base.SetValue(ReplicatorWorkflow.InputListProperty, value);
            }
        }

        public ReplicatorWorkflow()
        {
            InitializeComponent();
        }
        private void codeActivity1_ExecuteCode(object sender, EventArgs e)
        {
            Object data = String.Empty;
            if (sender is Activity)
            {
               if (((Activity)sender).Parent is ReplicatorActivity)
                {
                    ReplicatorActivity rep= ((Activity)sender).Parent as ReplicatorActivity;
                    data = rep.InitialChildData[rep.CurrentIndex];
                }
            }
            Console.WriteLine("CodeActivity instance data:  {0}", data);
        }
    }
}
1.Replicator活动的ExecutionType属性必须设置为Sequential或Parallel,ExecutionType属性指示复制的子活动
实例是以并行方式还是顺序方式执行。如果是以并行方式执行,则将在ReplicatorActivity活动执行时创建所有实例;如
果是以顺序方式执行,则将依次执行子活动的每个实例,即:前一个实例完成后在执行下一个实例。
2.必须填充InitialchildData集合属性,以便为子活动的每个复制实例提供数据。它会跟据InitialChildData集合中成
员的个数创建容器内Activity个实例个数,这个例子中我们使用依赖属性InputList来填充该集合。
3.如果设置UntilCondition 属性,当UntilCondition条件满足刊,Replicator会取消或中断正在运行的所有实例,结
束该结点.

宿主程序就不写了,执行的结果为CodeActivity会被多次执行,这取决你InitialchildData的数量。如下:
CodeActivity instance data:one
CodeActivity instance data:two
CodeActivity instance data:three

这里ExecutionType属性设置为Sequential,如果设置为Parallel,则就不正常了,会显示最后一个list的内容,如下:
CodeActivity instance data:three
CodeActivity instance data:three
CodeActivity instance data:three
原因是使用Sequential时子活动一次创建一个实例,执行完了一个在执行另一个,CurrentIndex的数值是逐渐增加的。如
果设置为Parallel,子活动是并行的。第一个活动刚执行的时候CurrentIndex的值就已经增加到最后一个了。
 
下面我们在来实现一个并行的Workflow,即将Replicator活动的ExecutionType属性设置为Parallel。工作流设计如下图:
01
这个例子中我们自定义一个活动ConsoleMessageActivity,它有一个Message属性可以接收工作流给每个实例传入的参数。
代码如下(ConsoleMessageActivity.cs):
using System; using System.ComponentModel; using System.Workflow.ComponentModel; namespace CaryWorkflows { public partial class ConsoleMessageActivity : Activity { public static DependencyProperty MessageProperty = System.Workflow.ComponentModel.DependencyProperty.Register( "Message", typeof(string), typeof(ConsoleMessageActivity)); [Description("A String message to write to the Console")] [Category("Flow Control")] [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public string Message { get { return ((string)(base.GetValue( ConsoleMessageActivity.MessageProperty))); } set { base.SetValue(ConsoleMessageActivity.MessageProperty, value); } }
public ConsoleMessageActivity() { InitializeComponent(); } protected override ActivityExecutionStatus Execute( ActivityExecutionContext executionContext) { if (Message != null) { Console.WriteLine(Message); } return base.Execute(executionContext); } } }


整个工作流的代码如下(ReplicatorParallelWorkflow .cs):
using System;
using System.ComponentModel;
using System.Workflow.ComponentModel;
using System.Workflow.Activities;
using System.Collections.Generic;

namespace CaryWorkflows
{
    public sealed partial class ReplicatorParallelWorkflow :
        SequentialWorkflowActivity
    {
        public static DependencyProperty InputListProperty
            = System.Workflow.ComponentModel.DependencyProperty.Register(
                "InputList", typeof(List<String>),
                typeof(ReplicatorParallelWorkflow));
        [Description("A list of strings to process")]
        [Category("Flow Control")]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public List<String> InputList
        {
          get
          {
            return ((List<String>)(base.GetValue(ReplicatorParallelWorkflow.InputListProperty)));
          }
          set
          {
            base.SetValue(ReplicatorParallelWorkflow.InputListProperty, value);
          }
        }
        public ReplicatorParallelWorkflow()
        {
            InitializeComponent();
        }
        private void replicatorActivity1_ChildInitialized(object sender, ReplicatorChildEventArgs e)
        {
            //ConsoleMessageActivity cma=((SequenceActivity)e.Activity).Activities[0] as ConsoleMessageActivity;

            ConsoleMessageActivity cma = e.Activity.GetActivityByName("consoleMessageActivity1") 
as ConsoleMessageActivity; if (cma != null) { cma.Message = e.InstanceData as String; } } } }
1.InputList:用于给工作流传入参数。
2.ChildInitialized事件:当子活动创建的时候执行,可以将工作流的InputList集合的值正确的传给
ConsoleMessageActivity活动中的Message属性。
下面我们看看如果找到并引用ConsoleMessageActivity活动:
首先使用ReplicatorChildEventArgs参数的Activity属性找到根活动,在这个工作流中就是Sequence活动,然后在通过
GetActivityByName方法找到ConsoleMessageActivity。
 

执行结果如下:
开始.......
First
Second
Third
结束.......

在ReplicatorActivity 中看起来好像是执行了三次ConsoleMessageActivity活动,但实际上它创建了原始活动的一个克隆,并且执行克隆后的版本,每个活动都是运行在自己的一个活动执行上下文中(ActivityExecutionContext )也叫AEC。
ActivityExecutionContext (AEC) 是在宿主应用程序调用 Start 方法时为活动创建的执行环境。AEC 提供了一种复合活动,该复合活动具有执行 (ExecuteActivity) 或取消 (CancelActivity) 子活动的能力。 它也可以通过 CloseActivity 方法来关闭自己。 这些是仅有的父活动可以通过 AEC 控制的执行状态更改。 所有其他活动状态都是由工作流运行时引擎控制的。AEC 具有名为 ExecutionContextManager 的属性,使其可以生成新 AEC。 这些 AEC 是父活动(如 WhileActivity 活动、ReplicatorActivity 活动或 ConditionedActivityGroup 活动)每次运行其子活动超过一次时生成的。 每次迭代都使用其自己的 AEC 创建一个克隆的活动,因此子活动的这些不同实例可以独立运行(而对于 ReplicatorActivity 活动则可能并行运行)。

我们来看本例中的活动树:
replicatorParalleWorkflow
         replicatorActivity1
                 SequenceActivity1
                         consoleMessageActivity1
这个是原始活动,每次执行都是以这个为模板进行深度克隆,克隆的活动树如下:
replicatorParalleWorkflow
         replicatorActivity1
                 SequenceActivity1(template A)
                         consoleMessageActivity1(template B)
                 SequenceActivity1 (cloned instance A1)
                         consoleMessageActivity1 (cloned instance B1)
我们在例子中
引用ConsoleMessageActivity活动是通过e.Activity.GetActivityByName("consoleMessageActivity") 得到的,我们也可以通过如下代码得到:
ConsoleMessageActivity cma= ((SequenceActivity)e.Activity).Activities[0]
                    as ConsoleMessageActivity;

ConditionedActivityGroup活动

CAG是一个复合活动,CAG会给它的子活动添加一个when条件,其子活动的执行顺序受应用于这些子活动的条件控制。CAG活动首次开始执行时,将计算其Until条件。如果Until条件的计算结果为false,则计算CAG活动所含的所有第一代子活动的When条件。如果某个活动的When条件的计算结果为true,则计划执行该活动。每次完成一个第一代子活动时,都会再次计算此Until和When条件。每次所执行的子活动完成执行时,都会重新计算 CAG活动的Until 条件,以及所有不处于执行状态的第一代子活动的Whe 条件。 因此,根据刚完成的活动中发生的情况,可以对其他第一代子活动确定新执行计划或重新确定执行计划。 只要CAG活动的Until 条件计算结果为true,就会立即取消当前正在执行的所有子活动。这个就不举例了。

InvokeWorkflowActivity,TerminateActivity,SuspendActivity活动

InvokeWorkflowActivity活动:使用 InvokeWorkflowActivity 活动可以从一个工作流中启动另一个工作流.我们需要设置其TargetWorkflow属性。WF中不支持递归调用工作流。
TerminateActivity,SuspendActivity活动:用于终止和挂起工作流。

上一篇:坚持学习WF(6):开发可复用的宿主程序
下一篇:坚持学习WF(8):本地服务之调用外部方法