WF 从入门到精通(第三章):workflow 实例

学习完本章,你将掌握:

1.使用不带参数和带参数二种方式初始化一个workflow 实例

2.测定你运行中的workflow 实例的状况

3.停止workflow 实例

4.确定你的workflow 空闲或终止的原因


一个workflow 实例由一个或多个活动组成。(我们将在第七章开始介绍各种活动:“Basic Activity Operations.”)“primary activity”或者“root activity”被称作

“workflow definition”。“workflow definition”通常的行为是为其它将要工作的活动充当一个容器。


注:“workflow definition”是你要求workflow 去执行的东西,而一个workflow 实例是一个正在执行的“workflow definition”。它们之间有明显的区别,一个正在执行当中,而另一个则不是。


workflow 实例从哪里来?它们当然应由你来创建。如你有困难来完成这个任务,并且自动创建的workflow 符合你的应用要求的话,也可由软件来完成,但至少你也要 写出workflow的任务或者workflow 运行时将为你执行的任务。Microsoft 提供了workflow 运行时,你还得创建余下的东西。毕 竟,这是你的应用。

WF 在上述这些地方的创建上可以为你提供帮助,WF 不仅将执行你创建的workflow 实例,而且也将帮助你去创建它 们。WF 集成了丰富的图形界面设计器,它能帮你以相同的方式把workflow 集成到你创建的ASP.NET Web Forms、Windows Forms 或者WPF 应用中。你可在工具箱上滚动鼠标,从许多活动项中选中一个,然后把它拖到设计界面上并释放它。假如这个活动项具有可配置的属性,你还 可使用Visual Studio 中的属性面板来配置它,使它符合你的意图。我们已在第一章简要地使用过workflow 设计器,在这里我们将再次使用它,毕竟与WF 相关的工 作几乎全是创建workflow 任务,workflow 可视化设计器的使用是开发过程中巨大的一个组成部分。


workflow 实例和任何其它软件类似。它们会开始执行、运行,直到遇到终止条件时终止。

这些或许是数据库中的所有记录已被处理,所有需 被压缩的档案已被压缩,或者workflow 发向各个审批方的文档已被批复(同意或不同意),或者是处理已经完成。它只有一个正常的启动位置,但有一个或 多个正常的可能停止的位置。

实例也能维持错误、异常。你可以处理这些异常也可不处理它。在某些情况下,或许你不想去处理出现的异常,并留到以后进行处理。

有 时,一个workflow 处理过程会执行很长很长时间才能完成。例如,一个处理过程发送了一份零件的订单并等待订单被接收。在workflow 终止前须确 认零件的型号和数目,而

这或许会花去几天,几周甚至几月。因此,难道一个workflow 实例也需要在内存里维持激活状态几天,几周或者几月吗?假如服务 器崩溃或电源断电怎么办?你的workflow 实例、数据、应用程序状态不是通通丢失了吗?

workflow 实例和组成实例的活动是workflow 处理过程中的重要部分。WF 已经为workflow实例的创建及执行提供了强大的支持。我们就来看看WorkflowInstance 对象。


WorkflowInstancWorkflowInstance对象介绍


workflowInstance 是一个 WF 对象,它为你提供了你的独立的workflow 任务上下文(环境)。你可使用这个对象去找到在你的处理任务中事情将是如何进行的。就像我们有方法和属 性去控制workflow 运行时一样,我们也有方法和属性并用它们和我们的workflow 实例进行交互。表3-1 列出了大多数 WorkflowInstance 属性,表3-2 列出了经常使用的方法。我们还将在第五章看到一些额外的属性和方法,“工作流跟踪”。


表3-1 WorkflowInstance 的属性

属性                         功能

InstanceId                 

    得到workflow 实例的唯一标识(一个Guid)

WorkflowRuntime     

    得到本workflow 实例的WorkflowRuntime


表3-2 WorkflowInstance 的方法

方法                         功能

ApplyWorkflowChanges

     通过WorkflowChanges 对象申请对workflow 实例进行更改。这允许你在workflow 执行时修改它(增加、移出或更改活动),当动态的更改实施时,workflow 实例会被暂停。

GetWorkflowDefinition 

    检索本workflow 实例的根(root)活动。

Resume

    恢复执行先前被暂停的workflow 实例。假如workflow 实例并未处于暂停状态,则不做任何事情。假如workflow 实例处在暂停状态,

workflow 

    运行时就会在workflow 实例刚被恢复后触发WorkflowResumed 事件。

Start

    启动一个workflow 实例的执行,在这个workflow 实例根活动上调用ExecuteActivity。假如Start 发生异常,它通过调用Terminate终止这个workflow 实例,并附加异常相关信息作为终止的原因。

Suspend

    同步暂停本workflow 实例。假如workflow 实例本就处于暂停状态,则不做任何事情。假如workflow 实 例正在运行,则workflow 运行时就暂停该实例,然后设置SuspendOrTerminateInfoProperty(说明原因)并进入 Suspend,触发WorkflowSuspended 事件。

Terminate

同步终止本workflow 实例。当宿主需要终止workflow 实例时,workflow 运行时就终止这个实例并试图 持久化实例的最终状态。然后WorkflowInstance 设置SuspendOrTerminateInfoProperty(说明原因)并进入 Terminate。最后,它触发WorkflowTerminated 事件并把终止原因传到WorkflowTerminateException 中的 Message 属性并包含到WorkflowTerminatedEventArgs 事件参数中。另外,假如在持久化时发生异常,workflow 运行时 取而代之地就把异常传到WorkflowTerminatedEventArgs 事件参数中。


还有更多和WorkflowInstance 相关的方法还未列出。到第六章“实例的加载和卸载”,

我们持久化工作流到数据库中时将看到他们的更多细节。


启动一个工作流实例

当我们启动一个workflow 实例前,我们必须有一个 workflow 任务让WF 去执行。在第一章,我们通过Visual Studio 为我们创建了一个基于workflow 的项目,它自动包含一个

workflow 任务,我们对它进行了修改以进行U.S.和加拿大邮政编码的验 证。如果需要的话,我们可以返回到那个项目去复制源代码,或者引用PCodeFlow.exe 程序集。然后我们就可直接使用这个已创建的 workflow。实际上,你可以这么去做。

然而,我们还是应该试着去学会写workflow 的应用。让我们通过使用一个包含延时的顺序 工作流去模拟一个长时间运行的任务吧。我们将在延时前执行一些代码,以弹出一个信息对话框。在经过延时后,我们将再次弹出一个信息对话框来指明我们的工作 已经结束。通过本书的学习过程,我们的例子将会越来越详细和丰富,但现在我们还处于入门阶段,我们还将保持我们的例子并把注意力更多的放到概念上而不是提 高技巧上。


注:记住,顺序工作流执行活动时一个接着一个。这个处理方式可和状态机工作流做下比较,状态机工作流执行活动时是基于状态的转变。假如你现在对此一片茫然的话,不用担

心,我们将在下章进入该主题。


在WorkflowHost解决方案中添加一个顺序工作流项目

1.启动Visual Studio 2008,加载上一章创建的名为“WorkflowHost”的解决方案准备进行编辑。

2.在解决方案中添加一个崭新的workflow 项目。

3.项目模板选择顺序工作流库。

4.项目名称起名为:LongRunningWorkflow。

现 在打开workflow 的视图设计器准备创建我们的workflow 任务。在视图设计器中的大图片中,我们将添加三个活动到这个新workflow 任务 中:两个Code 活动和一个Delay 活动。

Delay 活动将被放到两个Code 活动中间,目的是可让我们在Delay 执行前和执行后都将弹出一个信息对 话框。最初我们会指定一个合适的延时时间值,但稍后我们将对workflow 任务进行修改,以使workflow 任务初始化时能接受我们专门指定的一个延 时时间值。


创建这个模拟需执行很长时间的顺序工作流

1.激活workflow 视图设计器,移动鼠标到工具箱中。

2.从工具箱中选择Code 活动,并把该组件拖拽到workflow 设计器的表面。

image.png

3.释放鼠标并让Code 活动组件落到该顺序工作流中。

4.就像在第一章一样,我们将添加一些代码到Code 活动中以使worflow 任务经过这个活动时执行。在此单击Code 活动以确保该活动的属性面板已被激活。

5.在属性面板中激活ExecuteCode 属性的下拉编辑框,它将允许你命名将被触发的事件,该事件在Code 活动中的代码执行时触发。

6.输入“PreDelayMessage”。这样就添加了一个事件到worflow 代码中。稍候,我们将修改这段代码以显示一个信息对话框。但现在我们仍继续在workflow 的视图设计器上工作,因为我们需要添加另外两个活动。

image.png

7.从工具箱中选择Delay 活动并添加到Code 活动的下面。

image.png

注: 顺序活动,就像我们现在所做的工作一样,是以顺序的方式执行活动。顺序由workflow 视图设计器中活动的位置决定。在workflow 设计器窗口的顶 部的活动首先执行,

对于其它活动的执行顺序则按到视图设计器窗口底部的走向(箭头)决定。在下章我们还将重温这一过程。


8.我们需要为我 们的Delay 活动建立一个延时时间值。为此,我们要在Visual Studio属性面板中改变TimeoutDuration 的属性。把最后两个“00”改为“10”,意思是Delay 活

动将等待10 秒钟才允许 workflow 继续下一步的处理。

9.现在我们需要添加第二个Code 活动来显示第二个信息对话框。为此,重复步骤2 和步骤6 添加一个 新的Code 活动,但设置ExecuteCode 的属性为“PostDelayMessage"来作为事件的命名。以下是在workflow 视图设计器中 展示的workflow 的最终结果:

image.png

我 们还剩下两个任务未完成。最终,我们需要把我们的workflow 程序集引入到我们的主应用程序中以便执行它。但首先,我们必须添加必要的代码以显示那两 个信息对话框。我

们已经在我们的workflow 代码创建了两个事件:PreDelayMessage 和PostDelayMessage。我们将为它们 添加事件处理代码,在里面实际上就是弹出信息对话框的代码。


为延时前和延时后的事件添加代码


1.单击LongRunningWorkflow 项目中的Workflow1.cs 文件,查看其代码。

2.添加“System.Windows.Forms"的引用,并在Workflow1.cs 文件声明以下名称空间:

using System.Windows.Forms;

3.定位到新插入的PreDelayMessage 方法,在方法中插入以下代码:

MessageBox.Show("正在执行延时前的代码。");

4.和上一步类似,定位到新插入的PostDelayMessage 方法,在方法中插入以下代码:

MessageBox.Show("正在执行延时后的代码。");

假 如你这时编译这个解决方案,那没有任何错误,但是WorkflowHost 应用程序仍旧会像先前一章一样挂起。为什么呢?因为尽管我们创建了一个我们能够 使用的workflow 程序

集,但我们并未请求主应用程序去执行它。WorkflowCompleted 事件从未被触发,因此自动重置事件也就不会释放应 用程序主线程。

为执行我们的workflow 任务,我们要引用我们新创建的workflow 程序集并添加代码,以使WorkflowRuntime 对象来揭开workflow 任务工作的序幕。我们现在就开始吧。


宿主一个自定义workflow程序集并启动一个不带参数的workflow实例

1.首先在项目WorkflowHost 中添加对项目LongRunningWorkflow 的引用。

2. 假如我们现在去编译这个应用程序,WorkflowHost 将编译失败。为什么呢?原因是在前一章我们创建WorkflowHost 项目时,我们仅仅添加 了必须的引用以支持当时的环境编译通过。但现在,我们添加了一个“System.Workflow.Runtime”引用。同时又引入了一个现成的 workflow 程序集到我们的宿主应用程序中,因此我们需要为WorkflowHost 项目添加更多的和workflow 相关的引用。我们需要添加的引 用有“System.Workflow.Activities”和“System.Workflow.ComponentModel”。

image.png

3.打开Program.cs 文件并定位到Main 方法内的下面代码上:

Console.WriteLine("等待workflow 完成。");

4.在上述代码下面添加以下代码:

WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1)
);
instance.Start();

5.编译并执行WorkflowHost 应用程序。

执行结果如下:

image.png

让我们回到下面非常关键的代码上:

WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1)
);
instance.Start();

在这里,我们使用了WorkflowRuntime 对象的CreateWorkflow 方法创建了一个我们想去执行的workflow 实例。当我 们得到了一个返回的WorkflowInstance 对象后,我们调用了它的Start 方法来初始化workflow。注意这个workflow 实例不需 要我们预先输入参数就能执行。如果能预先输入一个可变的延时值那该多好?下一节我们将讨论这一话题。


启动一个带参数的workflow实例

带 输入参数启动的Workflow 实例把接收到的参数和相关的公有属性对应起来。就是说,为传入一个可变的延时值,我们只需在我们的workflow 实例上 创建一个公有的名为

“Delay”属性,并在创建这个实例时提供延时值即可。假如你对XML 序列化和.NET 中的“XmlSerializer”熟悉的 话,创建一个workflow 实例的过程就和把XML 流反序列化成一个.NET 对象的过程相似。事实上,这几乎差不多。

期望被传入workflow 实例的参数值通常存储在一个Dictionary 对象的Values 中,Dictionary 对象的关键字使用string 类型,对应的值使用简单的Object 对象。典型的代码

如下:

Dictionary<string,object> parms = new Dictionary<string,object>();

然 后你可使用Dictionary 对象的Add 方法添加参数。关键字必须是一个string,它表示workflow 的root 活动所暴露的公有属性的名 称。另外,对应的值的类型必须和活动的属

性类型一致。例如,我们传入一个整形类型的延时值并且我们的workflow 实例中暴露了一个名为Delay 的属 性和其对应,那添加一个参数到Dictionary 中的代码就应像下面的一样:

parms.Add("Delay",10); //延时10 秒。

我 们再次来写一些代码吧,我们相对做些小的修改,但这会获得许多功能。我们以控制台命令行的方式接收我们输入的一个整形数值作为延时值。为使我们的程序不会 永远运行下去,我们会把这个值限制在0 到120 之间,意思延时范围从0 秒到两分钟之间。我们也将对

workflow 增加Delay 属性。让我们一起来对我 们的workflow 组件做第一次修正。

为workflow 添加一个输入属性

1.打开Workflow1.cs 文件准备编辑。

2.在Workflow1 的构造函数后,添加以下代码:

private Int32 _delay = 10;
public Int32 Delay
{
    get { return _delay; }
    set
    {
        if (value < 0 || value > 120)
            value = 10;
        if (ExecutionStatus == ActivityExecutionStatus.Initialized)
        {
            _delay = value;
            delayActivity1.TimeoutDuration = new TimeSpan(0, 0, _delay);
        }
    }
}

我们对传入的整形值进行了检查,假如它超出范围,我们就指定一个默认值。我们检查了workflow 是否处在即将执行状态(不是已执行状态)。这 可防止有人在我们的workflow

运行中对延时值进行修改。我们也需要对Main 方法进行少量修改。我们需在命令行中输入一个参数作为延时值。假如它不 是一个整形值,我们就退出。否则,我们就接受它。假如它超过范围(0 到120 秒),我们就对它进行必要的约束(为默认值10 秒)。对Main 所做的修改步 骤如下:


启动一个带参数的workflow实例

1.打开Progrom.cs 文件准备编辑。

2.定位到下面的代码上:

workflowRuntime.WorkflowIdled += new EventHandler<WorkflowEventArgs>(workflowRuntime_WorkflowIdled);
workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(workflowRuntime_WorkflowCompleted);
workflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(workflowRuntime_WorkflowTerminated);

3.在上述代码后添加下面的代码:

Int32 delay = 0;
string val = args.Length > 0 ? args[0] : "10";
if (!Int32.TryParse(val, out delay))
{
    Console.WriteLine("你必须输入一个整形值!");
    return;
}
Dictionary<string, object> parms = new Dictionary<string, object>();

4.找到下面的代码:

WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1));

5.把上述代码改为:

WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1), parms);

编译并像在第一章那样运行试试,通过输入不同数值的延时时间,就可看到弹出的两个信息对话框所反映出的延时效果。


确定Workflow实例的状态


有趣的是,假如你看看workflow 运行时对象及workflow 实例对象的方法和属性,你找不到和状态相关的属性。你怎么知道是否有一个workflow 在执行呢?假如有一个,它处在那个状态呢?空闲吗?正执行当中吗?我们怎么确定?

我 将向前跳一小段,这其中大部分逻辑都放在workflow 状态的确定上。一个给定的workflow 实例的workflow definition 为您提供workflow 的执行状态。基类Activity 暴露

了一个ExecutionStatus 属性,它是一个 ActivityExecutionStatus 枚举的一个成员。下表

3-3 列出了ActivityExecutionStatus 的枚举值和相关的意 义。

表3-3 ActivityExecutionStatus 枚举值

image.png

image.png

表3-3 中的所有枚举值都涉及到一个活动对象,但你需记住workflow definition 就是一个活动。这意味着假如我们查询workflow definition 的状态,我们就能有效地确定整个

实例的状态。下面的过程演示了我们怎样添加相应代码来查询workflow definitely。


确定workflow实例执行状态

1.打开WorkflowHost 项目的Program.cs 文件准备编辑。

2.找到Main 方法并定位到下面的代码上:

instance.Start();

3.为了让我们看到workflow 实例的状态,我们直接查询workflow definition 的状态并把结果输出到控制台中显示出来。在上一步中定位到的代码下插入以下代码:

Console.WriteLine("workflow 处在:{0}状态。",

instance.GetWorkflowDefiniton().ExecutionStatus.ToString());


终止Workflow实例

假如你需要这样做的话,你也能容易地终止一个 workflow 实例,方法是通过执行workflow 实例对象的Terminate 方法。假如你在你的应用中添加了 WorkflowTerminated 的

事件处理,你就能从Exception 的Message 属性获取终止的原因。你将发现Exception 被包装到 WorkflowTerminatedEventArgs 中,并传入到WorkflowTerminated 的事件处理程序中。

这些代码 WorkflowHost 中已经包含了,我们还需添加一行代码来结束workflow 实例。


终止workflow 实例

1.打开Program.cs 文件,找到如下我们刚添加的代码上:

Console.WriteLine("workflow 处在:{0}状态。",

instance.GetWorkflowDefinition().ExecutionStatus.ToString();

2.在上述代码下添加以下代码:

instance.Terminate("用户取消");

假如你现在编译并运行WorkflowHost 程序,为他提供一个25 秒的延时值,你不会再看到任何一个信息对话框,控制台的输出结果如下:

image.png


Dehydration和Rehydration

在 我们离开workflow 实例的这一话题之前,我想再谈谈“dehydrating”和“rehydrating”一个实例的概念。假如你有一个长时间运 行的workflow 任务或者有大量的

任务执行,你就能卸载任务并把必须的执行环境信息存储到一个SQL Server 数据库中,这要用到运行在WF 之上的一个服务。

我们将在第六章详细讨论存储的问题,我在这提及它是因为,对一件事来说,处理的目标是workflow 实例。但另一方面,我们应听听这些术语,我不想让你在深入此书后却还不理解它们的基本意思。


当 你“dehydrate”一个实例时,你就正在把它从执行状态中移除并进行存储以便以后恢复。

典型的做法是使用WF 的持久化服务,但你也能写你自己的服务 来做同样的任务。以后当你的应用程序侦测到需重启workflow 实例时,你就“rehydrate”这个实例它就返回当

时的执行状态。这样做的原因有很 多,所有这些本书稍后都会简要说明。

源码下载 http://files.cnblogs.com/gyche/WorkflowHost2.rar


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

作者:hackpig

来源:www.skcircle.com

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


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

发表评论:

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

会员中心
搜索
«    2024年4月    »
1234567
891011121314
15161718192021
22232425262728
2930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 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