61阅读

c从入门到精通完整版-麓山文化《中文版会声会影X7从入门到精通》

发布时间:2018-04-25 所属栏目:IT计算机

一 : 麓山文化《中文版会声会影X7从入门到精通》

会声会影支持各类音频和视频编码,是公认的最简单好用的DV、影片剪辑软件。[www.61k.com]会声会影最新版本X7新增屏幕录制、html5优化、3d视频输出、无限条覆叠轨道等功能,新增转场28个,新增滤镜32个,增加数个渲染新格式,更增强了颜色管理,支持32位真彩色,兼容了其他的主流的颜色管理。会声会影X7也因此成为广大音视频编辑工作、爱好者更加得力的助手。

麓山文化为满足广大读者的需求,编写了会声会影X7系列教程,希望能给广大读者提供更加及时、实用的软件知识,帮助广大读者迅速实现技能提高。来看看X7到底有多好玩吧O(∩_∩)O

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影系列之《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

商品详情作 者:陈志民,麓山文化

出 版 社:机械工业出版社

出版时间:2014-10-1

版 次:4

开 本:16开

包 装:平装

内容推荐

本书结合5大综合实例+120个课堂举例+620分钟视频教学+1500个免费素材赠送,循序渐进地讲解了会声会影X7从获取素材、编辑素材、添加特效直到刻录输出的全部制作流程和关键技术,帮助读者在操作实践中轻松实现从入门到精通,从新手成为影像编辑高手。[www.61k.com]

目录

前 言

第1篇 影音入门篇

第1章 会声会影X7全新体验 1

第2章 会声会影X7快速入门 16

第2篇 视频剪辑篇

第3章 捕获精彩视频 35

第4章 素材的编辑与调整 47

第3篇 精彩特效篇

第5章 完美的视频覆叠 77

第6章 丰富的场景切换 95

第7章 神奇的滤镜特效 111

第4篇 后期编辑篇

第8章 主流字幕的制作 121

第9章 强大的音频效果 140

第10章 输出与共享 152

第5篇 案例实战篇

第11章 婚纱相册——真爱永恒 167

第12章 时尚写真——致我的青春 187

第13章 全家福相册——相亲相爱一家人 209

第14章 儿童相册——童年的美好时光 228

第15章 旅游记录——我的快乐假期 252

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

61阅读提醒您本文地址:

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

会声会影中文版 麓山文化《中文版会声会影X7从入门到精通》

微信公众号:LushanCulture

61阅读提醒您本文地址:

二 : Visio2007从入门到精通(中文版)


基本信息·出版社:电子工业出版社
·页码:480 页
·出版日期:2008年10月
·ISBN:7121072483/97871(www.61k.com]21072482
·条形码:9787121072482
·版本:第1版
·装帧:平装
·开本:16
·正文语种:中文
·外文书名:Special Edition Using Microsoft Office Visio 2007

内容简介《Visio 2007从人门到精通》(中文版)是Visio 2007完全手册,内容涵盖了 Visio 2007的方方面面,通过这本书,用户能全面掌握形状操作以及Visio的各种自定义功能;熟练应用Visio 2007主题;熟练地使用图层对Visio绘图进行分层处理;熟练地使用ShapeSheets和宏;熟练地使用Visi0 2007创建各种类型的绘图;学会如何连接数据库、Excel、Project,Outlook和大众网站;掌握如何创建和自定义数据图形和数据透视图表。
编辑推荐《Visio 2007从人门到精通》(中文版)Microsoft Office Visio 2007是一款功能强大的制图软件,它能帮助IT和商务专业人员轻松地可视化、分析和交流复杂信息。
目录
第1章 Visio的基础知识
欢迎使用Visio 2007

网上书店QC99COM

扩展:visio从入门到精通 / 吉他入门至精通中文版 / visio2007入门教程

三 : WF从入门到精通(中文版)

第一章 【翻译】WF从入门到精通(第一章):WF简介

学习完本章,你将掌握:

1.了解工作流的概念和理论

2.把WF和BizTalk与WCF做比较

3.开始使用WF进行编程

4.知道怎样使用Visual Studio工作流支持 以下是一小段进行邮政编码验证的代码

string USCode = @"^(\d{5}$)|(\d{5}$\-\d{4}$)";

string CanadianCode = @"[ABCEGHJKLMNPRSTVXY]\D[A-Z]\d[A-Z]\d";

public static bool ValidatePostalCode(string str)

{

return (Regex.IsMatch(str,USCode)||Regex.IsMatch(str,CanadianCode));

}

这没有什么特别的:“测试一个输入的字符串,如果为美国ZIP编码或者加拿大邮政编码则返回True,否则返回False”。这是一段很实用的代 码,事实上假如你不想在ASP.NET中使用其它验证控件的话,你就可在你的ASP.NET中使用这段验证逻辑。我们现在就创建一个Workflow应用 程序,它将进行同样的验证,并且返回验证是否通过的信息。 创建一个基于控制台的Workflow项目

1.启动Visual Studio 2008

2.创建顺序工作流控制台应用程序项目

3.项目名称中输入PCodeFlow

4.点击确定,将自动生成下面的初始界面

在编辑代码或插入Workflow项前,停留片刻看看Workflow项目为你生成的两个文件: Program.cs:从许多方面可以看出,这是一个典型的控制台应用程序源文件。然而,这个模板增加了大量代码来支持Workflow的操作。理解这些代码是本书的一个主要目标,但本章只是简单了解一下它做了些什么。

Workflow1.cs:这是一个Workflow组件,我们将对其进行修改以进行邮政编码的验证,

第一次使用你就可以放入许多东西,但我们现在还是从使用这个简单的Workflow开始工作吧。

创建一个工作流

在工具箱中拖一个IfElse活动组件到workflow

设计界面上。

我们现在将使用IfElse活动组件来问下面的问题:“我们已有的一个字符串是一个合法的邮政编码吗?”。我们在代码中将确实使用先前你看到的代码段中的正则表达式来问这一问题。

但 在我们进行这一步前请仔细看看workflow的视图设计器。workflow视图设计器提醒我们没有提供相应的代码来做这一决定。假如你看看命名为 “ifElseBranchActivity1”的左边分支的右上角,你将看到里面有一个惊叹号标记的小圆形图标。这是workflow视图设计器在告诉 你workflow并不完整。假如你试图编译带有这种提醒图标的项目时,将会编译报错。如你把鼠标移到图标上并单击呈现出的向下箭头时,你还能看到更多关 于这一错误情况

的信息。

在IfElse活动的分支上添加代码

1.移动鼠标到命名为“ifElseBranchActivity1”的左边分支上,单击以在属性面板上激活这一分支的属性。

2. 我们添加一个条件,意思是说workflow执行到分支时将强制其执行一些动作(条件判断为True时,将执行左边分支)。为做到这些,单击 “condition”属性激活条件类型属性的下拉列表。从列表中你可以选择“代码条件”类型、“(无)”类型和“声明性规则条件”类型。这里选择“代码 条件”类型。

3.上述步骤完成后,“condition”类型属性用户界面会包含一个“+”号,我们单击展开它的子属性,该子属性也命名为“condition”,单击以激活它。

4.“condition”属性需要输入我们想添加的内部事件名字。当条件需要判断时这个事件

将激发。在本例子中我们输入“EvaluatePostalCode”。

Visual studio 2008在幕后为你在workflow源文件中添加了你在“condition”属性中所指明的事件。稍候我们将添加在事件激发时所要执行的正则表达式代码段。

在我们做这些工作之前,让我们继续在workflow视图设计器上工作,我们刚刚增加了一个条件,它将引发工作流选择左边路径还是右边路 径。但是两条路径中都没有指明工作流将进行的动作。我们在左边“ifElseBranchActivity1”分支和右边

“ifElseBranchActivity2”分支中添加活动。

添加Code活动

1.拖一个“Code”活动到workflow视图设计器上,并放到左边分支

(ifElseBranchActivity1)的区域内。

2.就像先前添加条件判断的代码一样,我们将为该分支添加被选中时执行的代码。单击“codeActivity1”图标以在属性面板中激活它的属性。

3.在“ExecuteCode”属性中输入“PostalCodeValid”。

Visual Studio 2008会自动插入该事件。稍候我们会提供对应的执行代码。右边分支也同样做,只是要在“ExecuteCode”属性中输入“PostalCodeInValid”。

在我们的workflow中添加事件处理代码

1.打开Workflow.cs准备进行编辑

2.添加引用:using System.Text.RegularExpression;

3.定位到“EvaluatePostalCode”方法上,插入下面的代码: private void EvaluatePostalCode(object sender, ConditionalEventArgs e) {

e.Result = (Regex.IsMatch(_code, USCode) || Regex.IsMatch(_code, CanadianCode)); }

变量e是“ConditionalEventArgs”类型的实例,它用来告知“IfElse”活动应选择哪条路径。

4.我们也需要为workflow活动添加一种能力,以便接受输入的字符串来进行验证工作。为此我们添加下面的代码,我们将声明一个名为“PostalCode”的公有属性。 private string _code=string.Empty; public string PostalCode { set

_code = value;

}

有了这些,我们的workflow应用程序就可以进行编译了,但程序并不完整,我们还要在Workflow1.cs文件中定位到“PostalCodeValid”方法并插入下面的代码: Console.Write("The postal code {0} is valid.", _code);

同样在“PostalCodeInValid”方法中插入下面的代码:

Console.Write("The postal code {0} is *invalid*.", _code);

调用工作流

1.打开Program.cs文件。

2.定位到:

WorkflowInstance instance =

workflowRuntime.CreateWorkflow(typeof(PCodeFlow.Workflow1),wfArgs);

3.把上述代码替换为:

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

wfArgs.Add("PostalCode", args.Length > 0 ? args[0] : "");

WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(PCodeFlow.Workflow1),wfArgs); 编译应用程序

执行你的工作流应用程序

1.打开命令提示符窗口。

2.在命令提示符下定位到编译后所生成的应用程序目录。

3.输入pcodeflow 12345查看执行结果。

4.输入pcodeflow 1234x再看看执行结果。

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

第二章 【翻译】WF从入门到精通(第二章):workflow运行时

学习完本章后,你将掌握:

1.在你的应用程序中使用workflow

2.理解“WorkflowRuntime”对象的的基本功能

3.知道如何启动和停止workflow运行时

4.能够使用各种workflow运行时的相关事件

当 你在WF环境中执行任务时,需要一些东西来监管执行的过程,这个东西就是命名为“WorkflowRuntime”的对象。 WorkflowRuntime启动一个独立的工作流任务。在你的任务执行过程中,WorkflowRuntime也会针对不同的情况响应对应的事件。并 且,WorkflowRuntime还能在你的执行环境中增加一个附加的服务来保持跟踪。

WF

架构纵览见下图:

WF 和你的应用程序并行执行。其实,我们需要你的应用程序作为宿主。宿主应用程序可以是Windows Forms应用程序,控制台应用程序,ASP.NET WEB应用程序,甚至可以是一个Windows Server。WF运行时和你的应用程序同在一个.NET应用程序域执行,每个应用程序域只有一个唯一的WorkflowRuntime实例,试图在一个 应用程序域中建立第二个

WorkflowRuntime的实例的话,其结果就是抛出一个“InvalidOperationException”异常。

workflow 应用程序-“workflows”-意思指创建的逻辑上的一组活动。这些逻辑上的活动用来完成你需要的工作流任务。当你宿主workflow运行时的时 候,其实你就在操作工作流中的活动并让workflow运行时执行他们。其结果就是生成一个workflow实例,workflow实例是一个当前正执行 的workflow任务,它能自己完成逻辑上的一组活动,回忆第一章吧,活动能执行你提供的代码并且能对输入的数据做出相应的决定。下一章我们将简述工作 流实例,后面几章将对活动进行介绍。

在宿主应用程序中添加WF

一、创建一个名称为WorkflowHost的控制台应用程序项目

二、为项目添加名为System.Workflow.Runtime的引用

三、宿主workflow运行时

1.打开Program.cs文件准备编辑

2.在“using System.Text;”下添加以下代码:

“using System.Workflow.Runtime”

3.定位到“Main”方法,在里面添加以下代码:

WorkflowRuntime workflowRuntime=new WorkflowRuntime();

4.编译程序确认没有错误。在本章我们都将使用这一应用程序。

四、深入了解WorkflowRuntime对象

我们现在已经在我们的宿主应用程序中建立了一个 WorkflowRuntime类型的实例,该是简单的了解怎样和这个对象交互的时候了。和大多数有用的 对象一样,WorkflowRuntime也暴露了一些方法和属性,我们可用他们来控制Workflow运行时的环境。表2-1列出了所有 WorkflowRuntime属性,表2-2则列出了我们经常使用的方法。

表2-1 WorkflowRuntime的属性

属性 功能

用来指明workflow运行时是否已经启动并准备接受workflow实例。当宿主调用

IsStarted “StartRuntime”前IsStarted为False。期间它一直维持True直到宿主调用

“StopRuntime”为止。需注意的是当它正在运行中你不能增加核心服务。

获取或设置和WorkflowRuntime关联的名字。Workflow运行时正在运行中你不能

Name 设置这个属性(也就是说当IsStarted为True)。企图这样做的结果就是抛出一

个“InvalidOperationException”异常。

表2-2 WorkflowRuntime的方法

方法

AddService 功能 为workflow运行时添加指定的服务。能添加的服务类型和时间受到种种限

制。关于服务的详细信息将在第五章介绍。

创建一个workflow实例,它包含一些指定(但可选)的参数。假如workflowCreateWorkflow 运行时没有启动,该方法就调用StartRuntime方法。

GetWorkflow 通过 指明workflow实例的标识符(由一个Guid组成)来检索workflow实例。假如这个workflow实例是空闲和持久化保存的,它将被重新加载并执

行。

StartRuntime 启动workflow运行时和相关服务,并引发“Started”事件。

StopRuntime 停止workflow运行时和相关服务,并引发“Stoped”事件。

还有更多的关于WorkflowRuntime的方法,但表2-2中列出的方法是最经常用到的方法,也是我们将重点关注的方法。在workflow运行期间,WorkflowRuntime也将在各种时间引发许多事件,但我们将在后面的章节中介绍。

创建一个Workflow运行时工厂

单 例和工厂设计模式的组合是强大的,因为工厂能保证只创建出一个曾创建的对象的单一实例,这正好符合我们的要求(在这里使用单例模式的原因主要是从效率上考 虑,其次一个应用程序域也只能只有一个WorkflowRuntime),因为WorkflowRuntime完全有可能在不同的应用当中加载和启动(例 如独立的应用模块)。让我们看看怎样创建一个

WorkflowRuntime工厂。

一、在项目中添加一个类型为类的新项,文件名为WorkflowFactory.cs。

二、在WorkflowFactory.cs源文件中添加如下的引用

using System.Workflow.Runtime; 三、在类中添加下面的代码:

//workflow runtime的单一实例

private static WorkflowRuntime _workflowRuntime = null;

private static object _syncRoot = new object(); 四、在上述代码后添加如下方法: //工厂方法 public static WorkflowRuntime GetWorkflowRuntime() {

//多线程环境下防止并发访问 { if (null == _workflowRuntime)

}

return _workflowRuntime;

}

五、为类加上Public关键字,为防止类被直接实例化,还必须为类加上static标记,如下所示:

public static class workflowFactory

启动workflow运行时

参 考表2-2,里面有一个StartRuntime方法,从我们的工厂对象中调用这个方法很有意义,外部对象要求workflow运行时对象无需处理或担心 运行时环境状态的初始化。我们需要在我们的应用程序通过这一步来建立我们需要的workflow环境。外部调用对象也需要workflow运行时对象易于 使用。

并不是一定要调用StartRuntime。假如我们建立了一个workflow实例,StartRuntime实际上就已被调 用。假如我们曾经创建了一个workflow实例,或许并不用担心需要明确的调用StartRuntime。但是,一旦我们添加服务时,明确地调用它就很 有必要,因为可增强代码的可维护性并确信运行时环境的状态已建立,这样任何人就都能使用workflow运行时对象。

因此让我们在我们的工厂对象中做些轻微的更改并直接调用StartRuntime。

1.打开WorkflowFactory.cs文件并定位到下面的代码上:

_workflowRuntime = new WorkflowRuntime();

2.在上面的代码下添加以下的代码:

_workflowRuntime.Starttime();

停止workflow运行时

是 否有办法启动一个workflow运行时很有意义,如何停止一个workflow运行时也一样。

看看表2-2吧,里面有一个StopRuntime方法正 好符合我们要求。调用StopRuntime方法会卸载所有正执行的workflow和服务并关闭workflow运行时环境。当然,正确调用 StopRuntime位置是在你申请停止逻辑结束之前或者应用程序域关闭前调用。

1.打开WorkflowFactory.cs文件并定位到下面的代码上

_workflowRuntime = new WorkflowRuntime();

2.在上面代码的前面增加以下代码:

_workflowRuntime.Starttime();

3.在WorkflowFactory.cs中增加StopWorkflowRuntime事件处理函数: static void StopWorkflowRuntime(object sender, EventArgs e) { if (_workflowRuntime != null) if (_workflowRuntime.IsStarted) try catch (ObjectDisposedException) } } 以下是WorkflowFactory.cs文件的完整源代码,在第五章之前我们不会做更多的改变: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Workflow.Runtime; namespace WorkflowHost { //workflow runtime的单一实例

//工厂方法

//多线程环境下防止并发访问

if (null == _workflowRuntime) AppDomain.CurrentDomain.ProcessExit += new EventHandler(StopWorkflowRuntime); _workflowRuntime.StartRuntime(); } return _workflowRuntime; } static void StopWorkflowRuntime(object sender, EventArgs e) if (_workflowRuntime != null) if (_workflowRuntime.IsStarted) { try _workflowRuntime.StopRuntime(); catch (ObjectDisposedException) {

}

}

现在我们有了一个workflow运行时的创建工厂,然后我们将修改我们的主程序来使用它。

使用workflow运行时创建工厂

1.打开Program.cs文件并定位到如下代码上:

WorkflowRuntime workflowRuntime=new WorkflowRuntime();

2.把上面的代码修改成以下代码:

WorkflowRuntime workflowRuntime=WorkflowFactory.GetWorkflowRuntime(); 表2-3 workflow运行时的相关事件描述 事件

Started 功能 当workflow运行时启动后激发。

Stopped 当workflow运行时停止后激发。

WorkflowCompleted 当一个workflow实例完成后激发。 当一个workflow实例进入空闲状态时激发。当

workflow实例进入了空闲状态后,你就有机会把

他们从内存中卸载掉、存储到数据库并可在稍后

的时间把它们加载进内存。 WorkflowIdled

当一个workflow实例被终止后激发。在宿主中调

用一个workflow实例的Terminate方法、或通过WorkflowTerminated 一个Terminate活动、或当workflow运行时产生

一个未经捕获的异常时都会终止该workflow。

我们还将在第四章和第五章介绍更多的事件。

在我们为上面的事件添加相应的事件处理程序时,你会看到生成的代码和上一章我们创建的基于工作台的顺序工作流应用程序中的代码完全一样(或几乎完全一 样)。为了看看这些事件的作用,我们需要停止应用程序主线程一段时间。因此,我们使用一个基于内核的自动重置事件。一会儿后,我们将写出一些代码来使用上 述事件中的几个,你需要不时看看第一章中PCodeFlow项目中的Program.cs文件,对比它们的不同以及该写入什么样的代码。尽管它们并不完全 相同,但你在两个程序中还是能找到相同的内容。

处理workflow运行时事件

1.启动Visual Studio,打开项目的Program.cs源文件,定位到下面的代码上: WorkflowRuntime workflowRuntiem=WorkflowFactory.GetWorkflowRuntime();

2.假如你用过.NET的委托,下面的代码你将非常熟悉。我们需要为我们感兴趣的事件增加相应的事件处理程序。我们现在就来为workflow空闲时和完 成后增加相应的事件处理程序。稍候我们还会增加我们所需要的更多的事件处理程序。记住,下面的代码在步骤1定位的代码的下面:

workflowRuntime.WorkflowIdled += new

EventHandler<WorkflowEventArgs>(workflowIdled);

3.下面的代码添加了对workflow完成后的事件处理:

workflowRuntime.WorkflowCompleted += new

EventHandler<WorkflowCompletedEventArgs>(workflowCompleted);

4.现在添加对workflow终止后的事件处理:

workflowRuntime.WorkflowTerminated += new

EventHandler<WorkflowTerminatedEventArgs>(workflowTerminated);

5.假如你编译并运行WorkflowHost(本项目),这个应用程序能通过编译并运行。但没有执行workflow,因为我们并未告知 workflow运行时去启动一个workflow实例(我们将在下章添加)。为以后做准备,我们还要添加一些代码。首先,为了激发workflow中的 事件(以便我们观察它们),我们需要停止主线程足够长的时间,因此我们还将添加自动重置事件。在步骤3、4的代码下添加以下代码。

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

waitHandle.WaitOne();

Console.WriteLine("完成.");

6.在Main方法前定义一个名为waitHandle的静态成员:

private static AutoResetEvent waitHandle = new AutoResetEvent(false);

7.添加名称空间:

using System.Threading;

8.由Vistual Studio 2008创建的上面三个事件对应的事件处理程序内都包含“throw new NotImplementedException();”。我们需要移除这些代码并定位到workflowIdled的事件处理程序内,写入下面的代码:

Console.WriteLine("workflow实例空闲中");

9.定位到workflowCompleted的事件处理程序内,写入下面的代码:

Console.WriteLine("workflow实例已完成");

waitHandle.Set();

10.定位到workflowTerminated的事件处理程序内,写入下面的代码:

Console.WriteLine("workflow实例已终止,原因:'{0}'。",e.Exception.Message); waitHandle.Set();

完整的代码见列表2-2。

列表2-2 WorkflowHost应用程序的完整代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Workflow.Runtime; using System.Threading; namespace WorkflowHost { private static AutoResetEvent waitHandle = new AutoResetEvent(false); WorkflowRuntime workflowRuntime = WorkflowFactory.GetWorkflowRuntime(); workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(workflowRuntime_WorkflowCompleted); workflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(workflowRuntime_WorkflowTerminated); Console.WriteLine("完成."); }

{

Console.WriteLine("workflow实例空闲中");

Console.WriteLine("workflow实例已完成"); waitHandle.Set(); static void workflowRuntime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e) Console.WriteLine("workflow实例已终止,原因:'{0}'。",e.Exception.Message); }

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对象。

WorkflowInstanceWorkflowInstance对象介绍 对象介绍

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

表3-1 WorkflowInstance的属性

属性

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

WorkflowRuntime 得到本workflow实例的WorkflowRuntime

表3-2 WorkflowInstance的方法

方法 功能

通过WorkflowChanges对象申请对workflow实例进行更改。这允许

ApplyWorkflowChanges 你在workflow执行时修改它(增加、移出或更改活动),当动态的

更改实施时,workflow实例会被暂停。

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

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

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

WorkflowResumed事件。

启动一个workflow实例的执行,在这个workflow实例根活动上调

用ExecuteActivity。假如Start发生异常,它通过调用Terminate

终止这个workflow实例,并附加异常相关信息作为终止的原因。 Start

Suspend 同步暂停本workflow实例。假如workflow实例本就处于暂停状态,则不做任何事情。假如workflow实 例正在运行,则workflow运行

时就暂停该实例,然后设置SuspendOrTerminateInfoProperty(说

明原因)并进入 Suspend,触发WorkflowSuspended事件。

同步终止本workflow实例。当宿主需要终止workflow实例时,

workflow运行时就终止这个实例并试图 持久化实例的最终状态。然

后WorkflowInstance设置SuspendOrTerminateInfoProperty(说明

原因)并进入 Terminate。最后,它触发WorkflowTerminated事件Terminate 并把终止原因传到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设计器的表面。

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

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

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

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

因为我们需要添加另外两个活动。

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

活动的下面。

注: 顺序活动,就像我们现在所做的工作一样,是以顺序的方式执行活动。顺序由workflow视图设计器中活动的位置决定。在workflow设计器窗口的顶 部的活动首先执行,对于其它活动的执行顺序则按到视图设计器窗口底部的走向(箭头)决定。在下章我们还将重温这一过程。

8.我们需要为我 们的Delay活动建立一个延时时间值。为此,我们要在Visual Studio

属性面板中改变TimeoutDuration的属性。把最后两个“00”改为“10”,意思是Delay活动将等待10秒钟才允许 workflow继续下一步的处理。

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

的最终结果:

我 们还剩下两个任务未完成。最终,我们需要把我们的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”。

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

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

4.在上述代码下面添加以下代码: WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1));

instance.Start();

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

执行结果如下: 让我们回到下面非常关键的代码上:

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 { set if (value < 0 || value > 120) _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)) {

}

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枚举值 属性

Canceling

Closed

Executing 功能 活动正在取消中。 活动已被关闭。 活动当前正在运行。

Faulting Initialized 表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秒的延时值,你不会再看到任何一个信息对话框,控制台的输出结果如下:

DehydrationDehydration和Rehydration Rehydration

在 我们离开workflow实例的这一话题之前,我想再谈谈“dehydrating”和

“rehydrating”一个实例的概念。假如你有一个长时间运 行的workflow任务或者有大量的任务执行,你就能卸载任务并把必须的执行环境信息存储到一个SQL Server数据库中,这要用到运行在WF之上的一个服务。

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

当 你“dehydrate”一个实例时,你就正在把它从执行状态中移除并进行存储以便以后恢复。典型的做法是使用WF的持久化服务,但你也能写你自己的服务 来做同样的任务。以

四 : WF从入门到精通(中文版)

第一章 【翻译】WF从入门到精通(第一章):WF简介

学习完本章,你将掌握:

1.了解工作流的概念和理论

2.把WF和BizTalk与WCF做比较

3.开始使用WF进行编程

4.知道怎样使用Visual Studio工作流支持 以下是一小段进行邮政编码验证的代码

string USCode = @"^(\d{5}$)|(\d{5}$\-\d{4}$)";

string CanadianCode = @"[ABCEGHJKLMNPRSTVXY]\D[A-Z]\d[A-Z]\d";

public static bool ValidatePostalCode(string str)

{

return (Regex.IsMatch(str,USCode)||Regex.IsMatch(str,CanadianCode));

}

这没有什么特别的:“测试一个输入的字符串,如果为美国ZIP编码或者加拿大邮政编码则返回True,否则返回False”。(www.61k.com)这是一段很实用的代 码,事实上假如你不想在ASP.NET中使用其它验证控件的话,你就可在你的ASP.NET中使用这段验证逻辑。我们现在就创建一个Workflow应用 程序,它将进行同样的验证,并且返回验证是否通过的信息。 创建一个基于控制台的Workflow项目

1.启动Visual Studio 2008

2.创建顺序工作流控制台应用程序项目

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

3.项目名称中输入PCodeFlow

wf WF从入门到精通(中文版)

4.点击确定,将自动生成下面的初始界面

在编辑代码或插入Workflow项前,停留片刻看看Workflow项目为你生成的两个文件: Program.cs:从许多方面可以看出,这是一个典型的控制台应用程序源文件。[www.61k.com)然而,这个模板增加了大量代码来支持Workflow的操作。理解这些代码是本书的一个主要目标,但本章只是简单了解一下它做了些什么。

Workflow1.cs:这是一个Workflow组件,我们将对其进行修改以进行邮政编码的验证,

wf WF从入门到精通(中文版)

第一次使用你就可以放入许多东西,但我们现在还是从使用这个简单的Workflow开始工作吧。[www.61k.com)

创建一个工作流

在工具箱中拖一个IfElse活动组件到workflow

wf WF从入门到精通(中文版)

设计界面上。

我们现在将使用IfElse活动组件来问下面的问题:“我们已有的一个字符串是一个合法的邮政编码吗?”。我们在代码中将确实使用先前你看到的代码段中的正则表达式来问这一问题。

但 在我们进行这一步前请仔细看看workflow的视图设计器。workflow视图设计器提醒我们没有提供相应的代码来做这一决定。假如你看看命名为 “ifElseBranchActivity1”的左边分支的右上角,你将看到里面有一个惊叹号标记的小圆形图标。这是workflow视图设计器在告诉 你workflow并不完整。假如你试图编译带有这种提醒图标的项目时,将会编译报错。如你把鼠标移到图标上并单击呈现出的向下箭头时,你还能看到更多关 于这一错误情况

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

的信息。(www.61k.com)

在IfElse活动的分支上添加代码

1.移动鼠标到命名为“ifElseBranchActivity1”的左边分支上,单击以在属性面板上激活这一分支的属性。

2. 我们添加一个条件,意思是说workflow执行到分支时将强制其执行一些动作(条件判断为True时,将执行左边分支)。为做到这些,单击 “condition”属性激活条件类型属性的下拉列表。从列表中你可以选择“代码条件”类型、“(无)”类型和“声明性规则条件”类型。这里选择“代码 条件”类型。

3.上述步骤完成后,“condition”类型属性用户界面会包含一个“+”号,我们单击展开它的子属性,该子属性也命名为“condition”,单击以激活它。

4.“condition”属性需要输入我们想添加的内部事件名字。当条件需要判断时这个事件

wf WF从入门到精通(中文版)

将激发。在本例子中我们输入“EvaluatePostalCode”。

wf WF从入门到精通(中文版)

Visual studio 2008在幕后为你在workflow源文件中添加了你在“condition”属性中所指明的事件。[www.61k.com)稍候我们将添加在事件激发时所要执行的正则表达式代码段。

在我们做这些工作之前,让我们继续在workflow视图设计器上工作,我们刚刚增加了一个条件,它将引发工作流选择左边路径还是右边路 径。但是两条路径中都没有指明工作流将进行的动作。我们在左边“ifElseBranchActivity1”分支和右边

“ifElseBranchActivity2”分支中添加活动。

添加Code活动

1.拖一个“Code”活动到workflow视图设计器上,并放到左边分支

(ifElseBranchActivity1)的区域内。

2.就像先前添加条件判断的代码一样,我们将为该分支添加被选中时执行的代码。单击“codeActivity1”图标以在属性面板中激活它的属性。

wf WF从入门到精通(中文版)

3.在“ExecuteCode”属性中输入“PostalCodeValid”。

Visual Studio 2008会自动插入该事件。稍候我们会提供对应的执行代码。右边分支也同样做,只是要在“ExecuteCode”属性中输入“PostalCodeInValid”。

在我们的workflow中添加事件处理代码

1.打开Workflow.cs准备进行编辑

2.添加引用:using System.Text.RegularExpression;

3.定位到“EvaluatePostalCode”方法上,插入下面的代码: private void EvaluatePostalCode(object sender, ConditionalEventArgs e) {

e.Result = (Regex.IsMatch(_code, USCode) || Regex.IsMatch(_code, CanadianCode)); }

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

变量e是“ConditionalEventArgs”类型的实例,它用来告知“IfElse”活动应选择哪条路径。

wf WF从入门到精通(中文版)

4.我们也需要为workflow活动添加一种能力,以便接受输入的字符串来进行验证工作。[www.61k.com)为此我们添加下面的代码,我们将声明一个名为“PostalCode”的公有属性。 private string _code=string.Empty; public string PostalCode { set

_code = value;

}

有了这些,我们的workflow应用程序就可以进行编译了,但程序并不完整,我们还要在Workflow1.cs文件中定位到“PostalCodeValid”方法并插入下面的代码: Console.Write("The postal code {0} is valid.", _code);

同样在“PostalCodeInValid”方法中插入下面的代码:

Console.Write("The postal code {0} is *invalid*.", _code);

调用工作流

1.打开Program.cs文件。

2.定位到:

WorkflowInstance instance =

workflowRuntime.CreateWorkflow(typeof(PCodeFlow.Workflow1),wfArgs);

3.把上述代码替换为:

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

wfArgs.Add("PostalCode", args.Length > 0 ? args[0] : "");

WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(PCodeFlow.Workflow1),wfArgs); 编译应用程序

执行你的工作流应用程序

1.打开命令提示符窗口。

2.在命令提示符下定位到编译后所生成的应用程序目录。

3.输入pcodeflow 12345查看执行结果。

4.输入pcodeflow 1234x再看看执行结果。

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

第二章 【翻译】WF从入门到精通(第二章):workflow运行时

wf WF从入门到精通(中文版)

学习完本章后,你将掌握:

1.在你的应用程序中使用workflow

2.理解“WorkflowRuntime”对象的的基本功能

3.知道如何启动和停止workflow运行时

4.能够使用各种workflow运行时的相关事件

当 你在WF环境中执行任务时,需要一些东西来监管执行的过程,这个东西就是命名为“WorkflowRuntime”的对象。(www.61k.com) WorkflowRuntime启动一个独立的工作流任务。在你的任务执行过程中,WorkflowRuntime也会针对不同的情况响应对应的事件。并 且,WorkflowRuntime还能在你的执行环境中增加一个附加的服务来保持跟踪。

WF

wf WF从入门到精通(中文版)

架构纵览见下图:

WF 和你的应用程序并行执行。其实,我们需要你的应用程序作为宿主。宿主应用程序可以是Windows Forms应用程序,控制台应用程序,ASP.NET WEB应用程序,甚至可以是一个Windows Server。WF运行时和你的应用程序同在一个.NET应用程序域执行,每个应用程序域只有一个唯一的WorkflowRuntime实例,试图在一个 应用程序域中建立第二个

WorkflowRuntime的实例的话,其结果就是抛出一个“InvalidOperationException”异常。

workflow 应用程序-“workflows”-意思指创建的逻辑上的一组活动。这些逻辑上的活动用来完成你需要的工作流任务。当你宿主workflow运行时的时 候,其实你就在操作工作流中的活动并让workflow运行时执行他们。其结果就是生成一个workflow实例,workflow实例是一个当前正执行 的workflow任务,它能自己完成逻辑上的一组活动,回忆第一章吧,活动能执行你提供的代码并且能对输入的数据做出相应的决定。下一章我们将简述工作 流实例,后面几章将对活动进行介绍。

在宿主应用程序中添加WF

wf WF从入门到精通(中文版)

一、创建一个名称为WorkflowHost的控制台应用程序项目

二、为项目添加名为System.Workflow.Runtime的引用

三、宿主workflow运行时

1.打开Program.cs文件准备编辑

2.在“using System.Text;”下添加以下代码:

“using System.Workflow.Runtime”

3.定位到“Main”方法,在里面添加以下代码:

WorkflowRuntime workflowRuntime=new WorkflowRuntime();

4.编译程序确认没有错误。(www.61k.com)在本章我们都将使用这一应用程序。

四、深入了解WorkflowRuntime对象

我们现在已经在我们的宿主应用程序中建立了一个 WorkflowRuntime类型的实例,该是简单的了解怎样和这个对象交互的时候了。和大多数有用的 对象一样,WorkflowRuntime也暴露了一些方法和属性,我们可用他们来控制Workflow运行时的环境。表2-1列出了所有 WorkflowRuntime属性,表2-2则列出了我们经常使用的方法。

表2-1 WorkflowRuntime的属性

属性 功能

用来指明workflow运行时是否已经启动并准备接受workflow实例。当宿主调用

IsStarted “StartRuntime”前IsStarted为False。期间它一直维持True直到宿主调用

“StopRuntime”为止。需注意的是当它正在运行中你不能增加核心服务。

获取或设置和WorkflowRuntime关联的名字。Workflow运行时正在运行中你不能

Name 设置这个属性(也就是说当IsStarted为True)。企图这样做的结果就是抛出一

个“InvalidOperationException”异常。

表2-2 WorkflowRuntime的方法

方法

AddService 功能 为workflow运行时添加指定的服务。能添加的服务类型和时间受到种种限

制。关于服务的详细信息将在第五章介绍。

创建一个workflow实例,它包含一些指定(但可选)的参数。假如workflowCreateWorkflow 运行时没有启动,该方法就调用StartRuntime方法。

GetWorkflow 通过 指明workflow实例的标识符(由一个Guid组成)来检索workflow实例。假如这个workflow实例是空闲和持久化保存的,它将被重新加载并执

行。

StartRuntime 启动workflow运行时和相关服务,并引发“Started”事件。

StopRuntime 停止workflow运行时和相关服务,并引发“Stoped”事件。

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

还有更多的关于WorkflowRuntime的方法,但表2-2中列出的方法是最经常用到的方法,也是我们将重点关注的方法。在workflow运行期间,WorkflowRuntime也将在各种时间引发许多事件,但我们将在后面的章节中介绍。

创建一个Workflow运行时工厂

单 例和工厂设计模式的组合是强大的,因为工厂能保证只创建出一个曾创建的对象的单一实例,这正好符合我们的要求(在这里使用单例模式的原因主要是从效率上考 虑,其次一个应用程序域也只能只有一个WorkflowRuntime),因为WorkflowRuntime完全有可能在不同的应用当中加载和启动(例 如独立的应用模块)。让我们看看怎样创建一个

wf WF从入门到精通(中文版)

WorkflowRuntime工厂。[www.61k.com)

一、在项目中添加一个类型为类的新项,文件名为WorkflowFactory.cs。

二、在WorkflowFactory.cs源文件中添加如下的引用

using System.Workflow.Runtime; 三、在类中添加下面的代码:

//workflow runtime的单一实例

private static WorkflowRuntime _workflowRuntime = null;

private static object _syncRoot = new object(); 四、在上述代码后添加如下方法: //工厂方法 public static WorkflowRuntime GetWorkflowRuntime() {

//多线程环境下防止并发访问 { if (null == _workflowRuntime)

}

return _workflowRuntime;

}

五、为类加上Public关键字,为防止类被直接实例化,还必须为类加上static标记,如下所示:

public static class workflowFactory

启动workflow运行时

参 考表2-2,里面有一个StartRuntime方法,从我们的工厂对象中调用这个方法很有意义,外部对象要求workflow运行时对象无需处理或担心 运行时环境状态的初始化。我们需要在我们的应用程序通过这一步来建立我们需要的workflow环境。外部调用对象也需要workflow运行时对象易于 使用。

并不是一定要调用StartRuntime。假如我们建立了一个workflow实例,StartRuntime实际上就已被调 用。假如我们曾经创建了一个workflow实例,或许并不用担心需要明确的调用StartRuntime。但是,一旦我们添加服务时,明确地调用它就很 有必要,因为可增强代码的可维护性并确信运行时环境的状态已建立,这样任何人就都能使用workflow运行时对象。

因此让我们在我们的工厂对象中做些轻微的更改并直接调用StartRuntime。

1.打开WorkflowFactory.cs文件并定位到下面的代码上:

_workflowRuntime = new WorkflowRuntime();

2.在上面的代码下添加以下的代码:

_workflowRuntime.Starttime();

停止workflow运行时

是 否有办法启动一个workflow运行时很有意义,如何停止一个workflow运行时也一样。

wf WF从入门到精通(中文版)

看看表2-2吧,里面有一个StopRuntime方法正 好符合我们要求。(www.61k.com)调用StopRuntime方法会卸载所有正执行的workflow和服务并关闭workflow运行时环境。当然,正确调用 StopRuntime位置是在你申请停止逻辑结束之前或者应用程序域关闭前调用。

1.打开WorkflowFactory.cs文件并定位到下面的代码上

_workflowRuntime = new WorkflowRuntime();

2.在上面代码的前面增加以下代码:

_workflowRuntime.Starttime();

3.在WorkflowFactory.cs中增加StopWorkflowRuntime事件处理函数: static void StopWorkflowRuntime(object sender, EventArgs e) { if (_workflowRuntime != null) if (_workflowRuntime.IsStarted) try catch (ObjectDisposedException) } } 以下是WorkflowFactory.cs文件的完整源代码,在第五章之前我们不会做更多的改变: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Workflow.Runtime; namespace WorkflowHost { //workflow runtime的单一实例

//工厂方法

//多线程环境下防止并发访问

wf WF从入门到精通(中文版)

if (null == _workflowRuntime) AppDomain.CurrentDomain.ProcessExit += new EventHandler(StopWorkflowRuntime); _workflowRuntime.StartRuntime(); } return _workflowRuntime; } static void StopWorkflowRuntime(object sender, EventArgs e) if (_workflowRuntime != null) if (_workflowRuntime.IsStarted) { try _workflowRuntime.StopRuntime(); catch (ObjectDisposedException) {

}

}

现在我们有了一个workflow运行时的创建工厂,然后我们将修改我们的主程序来使用它。[www.61k.com)

使用workflow运行时创建工厂

1.打开Program.cs文件并定位到如下代码上:

WorkflowRuntime workflowRuntime=new WorkflowRuntime();

2.把上面的代码修改成以下代码:

WorkflowRuntime workflowRuntime=WorkflowFactory.GetWorkflowRuntime(); 表2-3 workflow运行时的相关事件描述 事件

Started 功能 当workflow运行时启动后激发。

wf WF从入门到精通(中文版)

Stopped 当workflow运行时停止后激发。(www.61k.com]

WorkflowCompleted 当一个workflow实例完成后激发。 当一个workflow实例进入空闲状态时激发。当

workflow实例进入了空闲状态后,你就有机会把

他们从内存中卸载掉、存储到数据库并可在稍后

的时间把它们加载进内存。 WorkflowIdled

当一个workflow实例被终止后激发。在宿主中调

用一个workflow实例的Terminate方法、或通过WorkflowTerminated 一个Terminate活动、或当workflow运行时产生

一个未经捕获的异常时都会终止该workflow。

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

我们还将在第四章和第五章介绍更多的事件。

在我们为上面的事件添加相应的事件处理程序时,你会看到生成的代码和上一章我们创建的基于工作台的顺序工作流应用程序中的代码完全一样(或几乎完全一 样)。为了看看这些事件的作用,我们需要停止应用程序主线程一段时间。因此,我们使用一个基于内核的自动重置事件。一会儿后,我们将写出一些代码来使用上 述事件中的几个,你需要不时看看第一章中PCodeFlow项目中的Program.cs文件,对比它们的不同以及该写入什么样的代码。尽管它们并不完全 相同,但你在两个程序中还是能找到相同的内容。

处理workflow运行时事件

1.启动Visual Studio,打开项目的Program.cs源文件,定位到下面的代码上: WorkflowRuntime workflowRuntiem=WorkflowFactory.GetWorkflowRuntime();

2.假如你用过.NET的委托,下面的代码你将非常熟悉。我们需要为我们感兴趣的事件增加相应的事件处理程序。我们现在就来为workflow空闲时和完 成后增加相应的事件处理程序。稍候我们还会增加我们所需要的更多的事件处理程序。记住,下面的代码在步骤1定位的代码的下面:

workflowRuntime.WorkflowIdled += new

EventHandler<WorkflowEventArgs>(workflowIdled);

3.下面的代码添加了对workflow完成后的事件处理:

workflowRuntime.WorkflowCompleted += new

EventHandler<WorkflowCompletedEventArgs>(workflowCompleted);

4.现在添加对workflow终止后的事件处理:

workflowRuntime.WorkflowTerminated += new

EventHandler<WorkflowTerminatedEventArgs>(workflowTerminated);

5.假如你编译并运行WorkflowHost(本项目),这个应用程序能通过编译并运行。但没有执行workflow,因为我们并未告知 workflow运行时去启动一个workflow实例(我们将在下章添加)。为以后做准备,我们还要添加一些代码。首先,为了激发workflow中的 事件(以便我们观察它们),我们需要停止主线程足够长的时间,因此我们还将添加自动重置事件。在步骤3、4的代码下添加以下代码。

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

waitHandle.WaitOne();

Console.WriteLine("完成.");

6.在Main方法前定义一个名为waitHandle的静态成员:

wf WF从入门到精通(中文版)

private static AutoResetEvent waitHandle = new AutoResetEvent(false);

7.添加名称空间:

using System.Threading;

8.由Vistual Studio 2008创建的上面三个事件对应的事件处理程序内都包含“throw new NotImplementedException();”。[www.61k.com)我们需要移除这些代码并定位到workflowIdled的事件处理程序内,写入下面的代码:

Console.WriteLine("workflow实例空闲中");

9.定位到workflowCompleted的事件处理程序内,写入下面的代码:

Console.WriteLine("workflow实例已完成");

waitHandle.Set();

10.定位到workflowTerminated的事件处理程序内,写入下面的代码:

Console.WriteLine("workflow实例已终止,原因:'{0}'。",e.Exception.Message); waitHandle.Set();

完整的代码见列表2-2。

列表2-2 WorkflowHost应用程序的完整代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Workflow.Runtime; using System.Threading; namespace WorkflowHost { private static AutoResetEvent waitHandle = new AutoResetEvent(false); WorkflowRuntime workflowRuntime = WorkflowFactory.GetWorkflowRuntime(); workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(workflowRuntime_WorkflowCompleted); workflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(workflowRuntime_WorkflowTerminated); Console.WriteLine("完成."); }

{

Console.WriteLine("workflow实例空闲中");

wf WF从入门到精通(中文版)

Console.WriteLine("workflow实例已完成"); waitHandle.Set(); static void workflowRuntime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e) Console.WriteLine("workflow实例已终止,原因:'{0}'。[www.61k.com]",e.Exception.Message); }

wf WF从入门到精通(中文版)

workflow集成到你创建的ASP.NET Web Forms、Windows Forms或者WPF应用中。(www.61k.com)你可在工具箱上滚动鼠标,从许多活动项中选中一个,然后把它拖到设计界面上并释放它。假如这个活动项具有可配置的属性,你还 可使用Visual Studio中的属性面板来配置它,使它符合你的意图。我们已在第一章简要地使用过workflow设计器,在这里我们将再次使用它,毕竟与WF相关的工 作几乎全是创建workflow任务,workflow可视化设计器的使用是开发过程中巨大的一个组成部分。

workflow实例和任何其它软件类似。它们会开始执行、运行,直到遇到终止条件时终止。这些或许是数据库中的所有记录已被处理,所有需 被压缩的档案已被压缩,或者workflow发向各个审批方的文档已被批复(同意或不同意),或者是处理已经完成。它只有一个正常的启动位置,但有一个或 多个正常的可能停止的位置。

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

有 时,一个workflow处理过程会执行很长很长时间才能完成。例如,一个处理过程发送了一份零件的订单并等待订单被接收。在workflow终止前须确 认零件的型号和数目,而这或许会花去几天,几周甚至几月。因此,难道一个workflow实例也需要在内存里维持激活状态几天,几周或者几月吗?假如服务 器崩溃或电源断电怎么办?你的workflow实例、数据、应用程序状态不是通通丢失了吗?

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

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

WorkflowInstanceWorkflowInstance对象介绍 对象介绍

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

表3-1 WorkflowInstance的属性

属性

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

WorkflowRuntime 得到本workflow实例的WorkflowRuntime

表3-2 WorkflowInstance的方法

方法 功能

通过WorkflowChanges对象申请对workflow实例进行更改。这允许

ApplyWorkflowChanges 你在workflow执行时修改它(增加、移出或更改活动),当动态的

更改实施时,workflow实例会被暂停。

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

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

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

WorkflowResumed事件。

启动一个workflow实例的执行,在这个workflow实例根活动上调

用ExecuteActivity。假如Start发生异常,它通过调用Terminate

终止这个workflow实例,并附加异常相关信息作为终止的原因。 Start

wf WF从入门到精通(中文版)

Suspend 同步暂停本workflow实例。(www.61k.com)假如workflow实例本就处于暂停状态,则不做任何事情。假如workflow实 例正在运行,则workflow运行

时就暂停该实例,然后设置SuspendOrTerminateInfoProperty(说

明原因)并进入 Suspend,触发WorkflowSuspended事件。

同步终止本workflow实例。当宿主需要终止workflow实例时,

workflow运行时就终止这个实例并试图 持久化实例的最终状态。然

后WorkflowInstance设置SuspendOrTerminateInfoProperty(说明

原因)并进入 Terminate。最后,它触发WorkflowTerminated事件Terminate 并把终止原因传到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设计器的表面。

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

3.释放鼠标并让Code活动组件落到该顺序工作流中。[www.61k.com]

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

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

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

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

wf WF从入门到精通(中文版)

因为我们需要添加另外两个活动。

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

wf WF从入门到精通(中文版)

活动的下面。

注: 顺序活动,就像我们现在所做的工作一样,是以顺序的方式执行活动。顺序由workflow视图设计器中活动的位置决定。在workflow设计器窗口的顶 部的活动首先执行,对于其它活动的执行顺序则按到视图设计器窗口底部的走向(箭头)决定。在下章我们还将重温这一过程。

8.我们需要为我 们的Delay活动建立一个延时时间值。为此,我们要在Visual Studio

wf WF从入门到精通(中文版)

属性面板中改变TimeoutDuration的属性。[www.61k.com]把最后两个“00”改为“10”,意思是Delay活动将等待10秒钟才允许 workflow继续下一步的处理。

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

wf WF从入门到精通(中文版)

的最终结果:

我 们还剩下两个任务未完成。最终,我们需要把我们的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项目添加更

wf WF从入门到精通(中文版)

多的和workflow相关的引用。[www.61k.com)我们需要添加的引 用有“System.Workflow.Activities”和

wf WF从入门到精通(中文版)

“System.Workflow.ComponentModel”。

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

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

4.在上述代码下面添加以下代码: WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1));

wf WF从入门到精通(中文版)

instance.Start();

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

执行结果如下: 让我们回到下面非常关键的代码上:

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

instance.Start();

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

启动一个带参数的启动一个带参数的workflow实例 实例

wf WF从入门到精通(中文版)

带 输入参数启动的Workflow实例把接收到的参数和相关的公有属性对应起来。(www.61k.com)就是说,为传入一个可变的延时值,我们只需在我们的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秒。

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

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

1.打开Workflow1.cs文件准备编辑。 2.在Workflow1的构造函数后,添加以下代码: private Int32 _delay = 10; public Int32 Delay { set if (value < 0 || value > 120) _delay = value;

delayActivity1.TimeoutDuration = new TimeSpan(0, 0, _delay);

}

}

我们对传入的整形值进行了检查,假如它超出范围,我们就指定一个默认值。我们检查了workflow是否处在即将执行状态(不是已执行状态)。这 可防止有人在我们的workflow运行中对延时值进行修改。我们也需要对Main方法进行少量修改。我们需在命令行中输入一个参数作为延时值。假如它不 是一个整形值,我们就退出。否则,我们就接受它。假如它超过范围(0到120秒),我们就对它进行必要的约束(为默认值10秒)。对Main所做的修改步 骤如下:

启动一个带参数的启动一个带参数的workflow实例 实例

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

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

wf WF从入门到精通(中文版)

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)) {

}

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);

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

确定确定Workflow实例的状态 实例的状态

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

我 将向前跳一小段,这其中大部分逻辑都放在workflow状态的确定上。一个给定的workflow实例的workflow definition为您提供workflow的执行状态。基类Activity暴露了一个ExecutionStatus属性,它是一个 ActivityExecutionStatus枚举的一个成员。下表3-3列出了ActivityExecutionStatus的枚举值和相关的意 义。

表3-3 ActivityExecutionStatus枚举值 属性

Canceling

Closed

Executing 功能 活动正在取消中。 活动已被关闭。 活动当前正在运行。

wf WF从入门到精通(中文版)

Faulting Initialized 表3-3中的所有枚举值都涉及到一个活动对象,但你需记住workflow definition就是一个活动。[www.61k.com]这意味着假如我们查询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());

wf WF从入门到精通(中文版)

终止终止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秒的延时值,你不会再看到任何一个信息对话框,控制台的输出结果如下:

DehydrationDehydration和Rehydration Rehydration

在 我们离开workflow实例的这一话题之前,我想再谈谈“dehydrating”和

“rehydrating”一个实例的概念。假如你有一个长时间运 行的workflow任务或者有大量的任务执行,你就能卸载任务并把必须的执行环境信息存储到一个SQL Server数据库中,这要用到运行在WF之上的一个服务。

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

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

当 你“dehydrate”一个实例时,你就正在把它从执行状态中移除并进行存储以便以后恢复。典型的做法是使用WF的持久化服务,但你也能写你自己的服务 来做同样的任务。以

wf WF从入门到精通(中文版)

后当你的应用程序侦测到需重启workflow实例时,你就“rehydrate”这个实例它就返回当时的执行状态。[www.61k.com)这样做的原因有很 多,所有这些本书稍后都会简要说明。

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

第四章 【翻译】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

ExecutionStatus 功能 获取或设置用户定义的关于活动的描述。 获取或设置一个指明实例能否被执行和验证的值。 获取实例最后运行的结果(ActivityExecutionResult)。(有Canceled、Compensated、Faulted、None和Succeeded)。 得到workflow的状态,其为ActivityExecutionStatus的枚举值

(Canceling、Closed、Compensating、Executing、Faulting和

wf WF从入门到精通(中文版)

Initialized)之一。[www.61k.com]

Name

Parent 获取或设置活动实例的名称。 获取包含本活动的父活动。

WorkflowInstanceId 获取和该活动相关的workflow实例的标识符。

表4-2 活动(Activity)的方法

属性 功能

Cancel

Clone

Execute

GetActivityByName

Load

RaiseEvent 取消活动的执行。 返回活动的一个深拷贝。 以同步方式运行活动。 假如在一个组合活动上执行,本方法将返回组合活动中所包含的指定名称的活动。 从一个流中加载一个活动的实例。 触发一个和指定的依赖属性相关的事件。

触发和所引用的依赖属性相关的事件。RaiseEvent和

RaiseGenericEvent<T> RaiseGenericEvent的作用是一样的——第一个事件RaiseEvent直接

指出DependencyPropenty,而RaiseGenericEvent则是一个泛型版本。

Save 把活动保存到流中。

活动的方法通常都具有虚拟和受保护的属性。目的是你可去覆盖它们,使其提供一个符合你自己的活动所需要的实现。目前为止,最关键的方法是Execute。当workflow运行时调用这个方法时,你的活动便开始执行了。

活 动可被分为两个大类:组合活动和基本活动。组合活动包含其它活动。一个极好的例子是我们目前为止贯穿书中的Sequential活动(译者注:它是基于顺 序工作流中所有活动的载体,在创建一个顺序工作流时Visual Studio就已为我们创建好了,可在视图设计器中看到)。目前为止所有的程序实例执行workflow实例的方式都是Sequential活动,它包含 其它活动,如它自身、Delay活动和Code活动。

基本活动,就像我刚谈到的Delay活动和Code活动,它们是一个基于单一任务的活动,我在本章早些时候谈过它。最终,你需要基本活动去实际承载特定的任务。组合活动或许可指挥任务和数据的流动,但基本活动能做更多。

ActivityExecutionContextActivityExecutionContext对象 对象

许多Activity对象的方 法需要一个ActivityExecutionContext对象来进行输入。在workflow运行时把你要执行的workflow实例入队的时候 ActivityExecutionContext对象被创建,因此,它不是你直接要创建的对象。workflow运行时为你创建它。

ActivityExecutionContext对象的作用是提供活动以方法和服务,以便和workflow实例挂钩。这些如初始化,定时器和产生执行流。它本质上是一个helper对象。在13章将更详细的对活动上下文(环境)进行讨论。

备 注:假如你熟悉ASP.NET编程的话,这个context对象本质上和

System.Web.HttpContext对象的作用是一样的。其它相似的还 有

System.Threading.Thread.CurrentContext。所有这些Context对象的目标都是一样的:提供一个存储位置并容 易地恢复一个当前执行实例的信息。这种情况下,它是一个执行当中的活动的一个实例。

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

wf WF从入门到精通(中文版)

依赖属性依赖属性(依赖属性(Dependency Properties)Dependency Properties)

在表4-2中,你将看到一些依赖属性(DependencyProperty)。[www.61k.com)什么是依赖属性呢? 通常,假如你为类创建了一个属性的话,你也会在类中创建一个字段来存储该属性的值。普遍的代码如下: class MyClass {

}

字段_x更正式的叫法是backing store。在这个例子中,你的类为X属性提供了backing store。 然而,WF和WPF通常都非常需要去访问你类中的属性。WPF需要指明容器中控件的空间和大小以便能最佳地被render。WF需要依赖属性来方便地进行活动绑定。WF中ActivityBind类可为你方便地进行活动绑定。

活动验证活动验证 活动验证

活动通常都具有验证能力,你可回忆第一章。

在第一章的例子中 有这样一种情况,如在IfElse活动中未指定应该选择哪一个分支进行执行的条件时,Visual Studio会提醒我们。其它活动实现了不同的验证算法。假如我们编译带有验证错误的代码,我们的编译都会失败。我们必须纠正这些验证条件不充分的代码, 才能编译和执行我们的workflow代码。

workflowworkflow类型 类型

你已创建过workflow应用程序,因此你可能注意到可以创建不同类型的workflow应用。 workflow应用程序的类型很大程度上依赖于你选择的root活动。

尽管你注意到在新项目对话框中仅仅只有两种workflow类型的应用程序可供选择, 但实际运用中存在三种主要的类型。迄今为止本书中你已经创建过顺序工作流,因此它们并不神秘。

当你创建workflow时,你的活动以你规定的顺序执行。

另一种从新项目对话框中看到的workflow类型是状态机工作流。我将在本章讨论它的更多细节。

第三种workflow类型基于顺序工作流,但它是规则驱动的。它不是仅仅执行你指定的任务,

而是由Policy活动和规则条件组成的基于规则的workflow,来执行基于你指定的业务规则workflow任务。

我们将在12章更多地学习这种workflow类型:“Policy活动”。因为这种类型的workflow以顺序活动作为root,

因此在新项目对话框中没有这种类型的workflow应用程序的模板可供选择。你应以顺序工作流作为起始,

然后增加基于规则的活动。

wf WF从入门到精通(中文版)

选择一种选择一种workflow类型 类型

在什么情况下一种类型的workflow比另一种类型的workflow更好?你如何选择合适的workflow类型呢?

表4-3可为你提供一些基本的参考。[www.61k.com]

表4-3 选择基本的workflow类型的判定表 workflow类适用条件 型

workflow任务可以自治的执行,很少由外部进行控制。主要由workflow自身来

顺序工作流 对执行的任务进行控制。只有少量用户或没有用户来和它进行交互。它的root

活动是SequentialWorkflow活动。

workflow任务严重依赖外部来控制和指示其执行。预期有很多的用户交互(或状态机工作其它外部控制)。对于基于状态的workflow,root活动是StateMachineWorkflow流 活动。

业务逻辑中包含复杂的判断条件,既不像顺序工作流也不像状态机工作流。基基于规则的于规则的工作流或者有一个顺序的root活动,或者有一个基于状态的root活工作流 动。

顺序工作流的理想应用是去执行业务处理。假如你需要从源中读数据,处理这些数据,发送通知,往你的一个数据池中写入结果的话,顺序工作流或许将符 合你的需求。这并不意味着顺序工作流不适合处理依赖于用户交互的特定任务,如赞同或不同意之类的审批任务。其实这样一些的用户交互不应成为 workflow自身的关注焦点。

假 如你需要大量的用户交互,当你的workflow发送通知给用户或其它系统(有各种原因:通知、需要批复、选择一个选项等等)以使用户或其它系统进行响应 (它们的响应来自事件)时,状态机工作流可能是更好的选择。这些事件触发了workflow从一种处理状态转化到另一种处理状态。我将在本章后面及14章 (“基于状态的工作流”)更多地讨论这些。 最后一种workflow类型(我们将在12章看到)是基于规则的workflow。这些workflow基于业务规则判定是否进行转化,并判定转化后的目标是什么。这些workflow通常都预置了更加复杂的剧情。

你或许会认为所有的workflow都能以基于规则的工作流类型来创建,但我们通常并不总是使用这种方式进行创建。因为其它的workflow类型,如顺序工作流和状态机工作流,它们能更容易地创建和测试。

要用最合适的workflow类型来构建你的系统。通常,在许多真实案例中你会发现你自己使用了所有三种workflow类型的组合。

顺序活动顺序活动 顺序活动

让我们进一步深入顺序复合活动吧。尽管迄今为止我们使用这些活动贯穿本书,但 我在之前有意地拖延谈论关于它的更多内容。现在我们去理解了workflow运行时和workflow实例是怎样工作的,并且知道workflow实例是 我们正运行中的workflow活动的版本,我们能更好的了解发生了什么。

执行顺序活动意味着这些活动以一个指定的顺序执行。首先要做的 事最先执行,最后才做的事最后执行。一个顺序活动就像在根据目录执行。你需要记下首先要做的任务,接下来要做的任务和最后要做的任务。假如这些任务以顺序 活动的方式存储,WF将以你指定的顺序精准地执行每个任务。

` 备注:本书中我们不会看到以动态的方式添加活动,但你应知道这是可以做到的。 在 Visual Studio中,workflow的视图设计器可帮你展示你的workflow。当你创建一

wf WF从入门到精通(中文版)

个顺序工作流应用程序并在设计器中打开root活动时,你可把 任务放到屏幕的最上方以便首先被执行。(www.61k.com)那些朝向底部的任务将晚些执行。从可视化界面可看出,活动运行的顺序是从上到下。当然顺序活动还可以是一个复合活 动。

创建顺序工作流创建顺序工作流 创建顺序工作流

在本书中迄今为止我们已创建过一些顺序工作流应用程序,因此这里我不再创建它们。但我还是把完整的步骤重复一下。

建立一个顺序工作流应用程序

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

1.打开Microsoft Vistual Studio 2008。

2.在文件菜单上,选择新建项目。然后将呈现新项目对话框。

3.在项目类型面板中,展开Vistual C#树形节点,呈现出基于workflow项目的模板。

4.在模板面板中,点击顺序工作流控制台应用程序或顺序工作流库。前者创建一个可执行的应用程序并以控制台的方式执行,而后者创建一个动态链接库并在其它应用程序中使用。

5.输入你的项目或应用程序的名称。

6.输入或选择你想保存你的项目的所在路径。

7.点击确定,Visual Studio 2008将为你建立一个基本项目,其中包含workflow视图设计器用户界面。

然后,你就可方便地从工具箱中拖拽你需要的活动,调整它们的属性以符合你的需求。假如你需要增加更多workflow库的项目,你可参考我前一章中的描述,或者简单地直接在你的应用程序中增加一个新的workflow类。接下来我们还会看到大量的例子。

状态活动状态活动 状态活动

迄今为止在本书中我们还未看到过状态机工作流。14章完全把焦点放到基于状态的工作流的工作上,但我在这里将介绍一些概念,我们也会快速地创建一个基于状态的工作流。 看看这样一个术语:有限状态机。我们把这个术语分成三个词:有限、状态和机器。有限,意思是我们将进行转化的状态的数目是有限的。状态是我们的应用程序在事件发生时进行转化的逻辑条件。机器则意味自动化。我将用一个例子来阐明。

在 工程学校,或许会要求你使用有限状态机来设计一些数字系统。例如自动售货机和洗衣机。看看自动售货机,思考一下机器工作必须具有的步骤,以便它能为你提供 你需要的商品(如汽水、糖果、点心等等)。当你投入硬币时,它会合计你投入的硬币金额,直到你投入的硬币金额能购买商品时为止。当你选择一个商品时,它会 检查存货清单。假如有货,它就把你选择的东西分发给你。

我们可以使用有限状态机来构建自动售货机。在有限自动机的图示中,我们使用圆来表 示状态,箭头来表示状态之间的转换,转换由事件触发。图中有一个逻辑上的起点和一个或多个逻辑上的终点。假如我们停在其它地方,我们的应用程序就被称作未 指定状态或者无效状态。我们的工作就是防止无效状态,如我们不能免费地获取商品,我们也不应该接收超过商品价格的多余的钱。假如自动售货机接受了钱但又未 提供商品的话,用户毫无疑问会暴怒。 假象一下简化的自动售货机,让我们画出状态和导致状态转换的事件吧。正如我提到的,状态用圆来表示。使你的机器从一个状态变为另一个状态的事件用箭头表示。它们都可命名

wf WF从入门到精通(中文版)

以便我们知道这些是什么状态和相关的转换。我们毫无疑问需要一个开始状态,如下图:

图4-2 有限状态机起始状态符号

wf WF从入门到精通(中文版)

这 个状态表示机器所处的这样一个位置:等待有人来投入一个硬币。(www.61k.com)因此看看当有人来投入一个硬币,但它还不够买一个商品的情况,我们通过创建一个新状态来进行 模拟,这个状态叫WaitCoins(等待硬币)状态,通过CoinInserted(投入硬币)事件转换到该状态,如图

wf WF从入门到精通(中文版)

4-3:

图4-3 转换到WaitCoins状态

在 用户投入足够金额的钱以能购买其中的商品之前,机器一直处在WaitCoins状态,并接受CoinInserted事件,否则会触发 SufficientCoins(金额足够)事件使我们的机器转到WaitSelection(等待选择)状态。在这里我们的自动售货机会耐心地等待用户 选择一个商品。(在实际生活中,用户也能在任何时候要回投入的硬币,为了简单起见,本例还是不考虑它吧。)

当用户选择商品后,商品会被分发给用户,我们的(状态)转换也就结束了。完成状态,或者称作结束状态,由二个圆圈来指明,参见图

wf WF从入门到精通(中文版)

4-4。

图4-4

尽 管这个自动售货机在现实世界中或许太过于简单,但此处只是期望为你提供一个的简要描述,使你明白状态机是如何工作的。当我们设计状态机时,我们指明其离散 的状态,或者逻辑位置来等待事件发生,然后我们指明转换机器状态的事件。有些事件可让机器返回到同一状态,如开始状态。其它事件则会在一个新的事件被处理 后使机器转换到一个新的状态。没有事件被触发就没有状态的转换,理想情况下应没有无法预料的事件或异常。

这种模型和我们用过的顺序工作流 模型有很大的不同。在顺序工作流里,活动以指定的顺序依次执行。一旦在该顺序链上的一个活动执行完它的任务,在该链上的下一个活动就开始执行它的工作。在 workflow处理中或许有事件参与其中,但它们在workflow任务的处理

wf WF从入门到精通(中文版)

(如定时器事件)上相对简单。(www.61k.com]

但状态机会花费大量时间在 等待上。它们等待事件,依赖事件来使它们的状态进行转换。状态自身不会激发事件(尽管它们可能会调用外部代码)。它们就是事件处理,因此它们会耐心地等待 它们需要的事件来进行状态的转换。依靠事件,它完全可能从一个状态切换到任何一个离散的不同的状态。假如我们的自动售货机处在WaitCoins状态,当 接受一个

CoinInserted事件、RefundRequested事件或ImminentPowerdown事件时会分别做不同的事情。但我并未在 图4-4中这个经过简化的模型里画出这些事件,但我相信你能看懂不同的事件是怎样驱动你的有限状态机转换到不同状态的。

在WF里,基于状态的workflow内的个别状态由State活动创建。State活动是一个复合(组合)活动,但它对容纳的子活动有限制。你将在14章学习到基于状态的workflow的更多东西。

备 注:正如顺序工作流使用一个特别的Sequence活动来容纳整个工作流一样,基于状态的工作流也有一个特别的root活动做这事,这就是 StateMachineWorkflow活动,它是一个特别的State活动。特别之处是它是必须的,这样当初始化执行时root活动就能接受初始化参 数。

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

创建一个状态机工作流应用程序创建一个状态机工作流应用程序 创建一个状态机工作流应用程序

怎样创建一个基于状态的workflow呢?创建一个基 于状态的工作流和创建一个顺序工作流一样容易。我们现在就来看看怎样创建一个状态机工作流。然而我们现在不会添加任何代码——虽然本书后面有很多时候需我 们这样去做,但我们现在只要需要了解怎样创建一个状态机工作流就行了。

创建一个状态机工作流应用

1.启动Microsoft Visual Studio 2008。

2.在文件菜单上,选择新建一个项目,这将打开新建项目对话框。

3.展开项目类型面板中的Visual C#节点,这将显示所有使用C#语言的项目类型。

4.在Visual C#节点下点击Workflow节点,这将显示所有基于工作流的项目模板。

5.在模板面板内,点击状态机工作流控制台应用程序或状态机工作流库。如下图所示:

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

6.输入程序或项目的名称。[www.61k.com)

7.输入或选择项目文件要保存的位置。

8.点击确定。Visual Studio 2008将为你创建一个包含workflow视图设计器用户界面

wf WF从入门到精通(中文版)

的项目,如下图:

我们需要去触发引发工作流改变状态的事件,因此,我们需理解workflow实例是怎样和它们的宿主应用程序进行通信的。我们将在第八章(“调用外部方法”)看到宿主和workflow之间的通信,在第十章(“事件活动”)中我们将学习状态机工作流的事件驱动。

wf WF从入门到精通(中文版)

第五章 【翻译】WF从入门到精通(第五章):workflow跟踪

学习完本章,你将掌握:

1.workflow的可选服务

2.创建一个事件跟踪数据库

3.激活事件跟踪服务

4.创建一个自定义跟踪

5.查看你的workflow的跟踪信息

目前为止,我们看过workflow的一些基本对象。[www.61k.com)我们通过活动创建workflow任务,它们在执行时由 WorkflowInstance对象进行管理。workflow实例由WorkflowRuntime编入队列并进行控制。但WF不只是为我们提供了这 些对象,它也为我们提供了一些服务来和这些对象一起协同工作。

可插拔可插拔(可插拔(可选)可选)服务 服务

工作流服务是一些附加的软件库,你的工作流能使用它来完成它们的的任务。有些服务是非必须可选的,如本章介绍的跟踪服务。而其它的服务需要你的工作流必须执行它。 一个可插拔服务是这样一个服务,它能像照菜单点菜一样被选中以执行特定任务。例如,有管理线程的服务、跟踪的服务、事务服务等等。你可选择那些适合你的工作流的服务。你甚至还能自己进行创建。

哪这些服务看起来像什么?他们能为我们做什么?表5-1列出了可获取的基本服务,它很好地为你描述了这些可获取的服务的概念,并告诉你他们能做什么。

当中的大部分服务我们不会直接使用。我们普遍的用法是使用从这些基本服务派生出的服务。

表5-1 基本工作流服务 服务 功能

该基类为你提供了一些方法,使你能用

来管理和一个工作流实例相关的工作流

队列。

抽象基类,派生出工作流运行时的内核

服务。

所有在工作流运行时宿主上创建线程以

运行工作流实例的类的基类。 WorkflowPersistenceService 抽象基类,派生出所有持久化的服务。 WorkflowQueuingService WorkflowRuntimeService WorkflowScheddulerService

那些管理订阅(Subscriptions)工作流WorkflowSubscriptionService 运行时类的基类。

WorkflowTransactionService 所有事务服务的基类。

TrackingService 一个抽象基类,在跟踪服务和运行时跟踪基础结构(infrastructure)间提供

了基本的接口。

请记住这些是基类。我们使用的服务实际上从它们派生。例如,当我们运行一个工作流实例时,有时需为实例创建一个线程去使用。 DefaultWorkflowSchedulerService正是做这

wf WF从入门到精通(中文版)

个工作的,它使用WorkflowSchedulerService作为它的基 类。(www.61k.com]但假如你想自己提供这个线程,你可使用ManualWorkflowSchedulerService代替。在本章中我们将看到由

SqlTrackingService提供的跟踪服务,它使用了TrackingService作为它的基类。

“可插拔(可选)”一词部分来源于下面的情况:你可能考虑在任何时间上你都可能需要使用一个调度程序服务,运行时服务,入队和订阅(定时器)服务。但你还能在工作中进一步添加持久化和跟踪服务,以及外部数据通信服务。

工作流跟踪工作流跟踪 工作流跟踪

在 本章,我们将把重点放到跟踪服务上。其它服务将在其它章节进行介绍。WF由一个主要的跟踪服务——SqlTrackingService承载。但是假如你 需要的话,也有两个额外的服务可用。它们是ConsoleTrackingService和SimpleFileTrackingService,这二个 服务允许你把跟踪信息写到控制台窗口或者文件中而不是Microsoft SQL Server数据库。在这里我们不会使用这两种服务,但你需要的话你可使用它们。

使用使用SqlTrackingService进行工作流事件跟踪 进行工作流事件跟踪

通 过添加一个跟踪服务(通常是SqlTrackingService)到工作流运行时中,你可跟踪你的工作流的处理过程。假如你有特定的跟踪需求,你也能创 建你自定义的跟踪事件。假如捕获的事件为你提供了过多的跟踪数据,你也能创建跟踪配置文件来过滤这些跟踪数据。 当跟踪的事件激发时,WF 创建并管理跟踪记录。尽管你不用做这些工作,但你还是能容易地从WF中直接访问这些跟踪记录。你要知道这些信息也被记录到数据库中,因此直接从数据库中检 索这些信息也是可能的。通常都在记录这些跟踪信息后的某个时间,使用一个象WorkflowMonitor或你自己设计的工具之类的外部跟踪监控工具,来 查询这些跟踪信息。 表5-2列出了在你的WF事件跟踪中经常使用的对象,在本章我们将使用其中的一些。假如你需要自定义你的工作流事件跟踪,那你应知道WF为你提供了一个和跟踪相关对象的强大类库。

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

表5-2 事件跟踪对象 对象

功能 指定要从活动中提取并在跟踪点匹配时与关联的批注集合一起发送到跟踪服务

的属性或字段。

表示一个条件,该条件通过使用指定的

ActivityTrackingCondition 比较运算符将活动成员的值与指定值进

行比较。

ActivityTrackingLocation 定义与根工作流实例的可能执行路径中的某个活动状态事件相对应的活动限定

位置。

包含运行库跟踪基础结构在

ActivityTrackPoint 匹配时发送到跟

踪服务的数据。它还用在

ActivityEvents 属性的返回列表中。

定义工作流实例的可能执行路径中要跟

踪的点,该点与活动执行状态更改关联。

包含用于管理跟踪数据查询的方法和属

性,跟踪数据包含在 ActivityTrackingRecord ActivityTrackPoint SqlTrackingQuery

wf WF从入门到精通(中文版)

SqlTrackingService 使用的 SQL 数据

库中。[www.61k.com)

包含一些属性,这些属性用于约束

SqlTrackingQuery.GetWorkflows 调用

所返回 SqlTrackingWorkflowInstance

对象的集合。 SqlTrackingQueryOptions

通过工作流实例的

SqlTrackingWorkflowInstance SqlTrackingService 提供对SQL数据库

中保留的跟踪数据的访问

定义根工作流实例的可能执行路径中的

关注点,应将有关该关注点的信息通知

跟踪服务。它过滤跟踪事件,并把过滤

后的跟踪记录返回给某个跟踪服务。这

里有三种类型的跟踪事件能被过滤:活

动状态事件、工作流状态事件和用户事

件。

定义与根工作流实例的可能执行路径中

的某个用户事件相对应的活动限定位

置。

包含运行库跟踪基础结构在

UserTrackPoint 匹配时发送到跟踪服

务的数据。

定义一个要跟踪的点(与用户事件关

联),该点位于根工作流实例的可能执

行路径中。 TrackingProfile UserTrackingLocation UserTrackingRecord UserTrackPoint

指定要从工作流的根活动中提取,并在

WorkflowDataTrackingExtract 跟踪点匹配时随关联的批注集合一起发

送到跟踪服务的属性或字段。

WorkflowTrackingLocation 定义对发生在根工作流实例中的特定工作流事件的关注;用于按跟踪配置文件

中的 WorkflowTrackPoint 进行匹配。

包含运行时跟踪基础结构在匹配了

WorkflowTrackPoint 时发送到跟踪服

务的数据。它还用在 WorkflowEvents

属性的返回列表中。 WorkflowTrackingRecord

定义一个与一组工作流状态事件关联的

WorkflowTrackPoint 点,这些事件在根工作流实例的可能执

行路径中进行跟踪。

这些对象可考虑归为两个大类:跟踪数据检索和跟踪详细说明。跟踪检索对象,如

SqlTrackingQuery,一旦跟踪数据被存储到数据库中,你可使用它们采集跟踪数据。跟踪详细说明对象,如跟踪点和位置对象,允许你能在工作流代码中控制该跟踪什么。

像 跟踪点和位置对象之类的跟踪详细说明对象还可被归为三大组:活动事件、工作流事件和用户事件。和活动相关的跟踪对象,如 ActivityTrackingPoint 或

ActivityTrackingLocation,用来记录相关联的活动的事件信息并保存到跟踪数据库中。这

wf WF从入门到精通(中文版)

些事件包含如下这些:活动取消、未处理的异 常和执行的事件。(www.61k.com]工作流事件跟踪对象的工作方式和工作流相关的事件的工作方式相像(但工作流启动和停止,实例的创建、空闲和完成及其它相似的相关联的事件 除外)。最后是用户事件跟踪,它用在自定义你特有的工作流跟踪需求中指定你的工作流并完全依赖于你的工作流想怎样进行跟踪。在本章中当我们学习跟踪配置文 件时会看到它们中的几个。

跟踪记录通过批注加以装饰。批注是一些保存跟踪记录并被记录进跟踪数据库的字符串。关联活动和关联工作流的跟踪记录有一个创建好的批注的集合,但你可以为用户关联事件的跟踪记录提供一个额外的批注。

在 WF中跟踪这一术语和平常“跟踪”的概念没有什么不同。平常意义上的“跟踪”是一个有用的调试工具,在ASP.NET、像WPF之类的.NET技术及 Windows Forms中都支持跟踪调试的能力。跟踪允许你过滤跟踪信息记录以满足你的需要,你既可只看异常的跟踪信息,也能看到整个跟踪栈。

WF跟踪基于相似的概念,事实上是过滤。正如你可能想到的,关联活动事件和关联工作流事件将产生所有类型的跟踪记录,你或许能从中找到感兴趣的记录(如未处理的异常或空闲状态),你可以决定其它的事件不用进行跟踪。

为 过滤掉你不想进行跟踪的事件,你要创建一个跟踪配置文件。一个跟踪配置文件是一个XML文档,它指明了跟踪的对象和要排除跟踪的对象。和跟踪不同,跟踪配 置文件指明哪些东西要写入跟踪数据库,而不是指明以后哪些东西能被查看到。假如你排除了一些事件,这些排除的事件就不会向数据库里写任何东西。和跟踪的另 一个不同之处是,跟踪配置文件的XML文档也被记录进跟踪数据库,当执行工作流时被恢复。换句话说,跟踪记录了指定的要去跟踪的任何东西,但不进行跟踪信 息归类。

设置设置SQL Server进行跟踪 进行跟踪

尽 管你可创建自定义的跟踪服务来把跟踪数据记录进各种存储中(如消息队列或数据文件),但本章,我们将把注意力放到SQL Server 2005数据库上,WF有能力把事件数据记录到SQL Server 2005数据库中。WF为使用SQL Server 2005提供了内置的创建支持。

我们先在SQL Server Management Studio(或者Express版本)中创建一个新的数据库。然后需运行一些由WinFX组件提供的SQL脚本,这些脚本将创建数据库角色、表和视图、 必须的存储过程以和你的工作流进行交互。我们就来通过创建一个新数据库并运行一些准备好的脚本来开始吧,然后我们将使用WF跟踪服务记录下跟踪数据并写入 数据库。

备注:我在下面的步骤中使用SQL Server Express,但这些步骤对于其它版本的SQL Server 2005同样适用。

创建一个创建一个SQL Server 2005跟踪数据库 跟踪数据库

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

1.启动SQL Server Management Studio,连接数据库引擎。

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

2.在数据库节点上单击右键激活右键快捷菜单,选择“新数据库”。[www.61k.com]

3.在新数据库对话框中输入“WorkflowTracking”作为数据库的名称字段,点击确定。

4. 下一步将执行WF为设置跟踪所提供的脚本(这会创建表、视图以及工作流跟踪的角色)。这些脚本的位置 在<%WINDIR%>\Microsoft.NET\Framework\3.0\Windows Workflow Foundation\SQL\ZH-CHS,在这里<%WINDIR%>是指你的Windows目录(通常是C:\Widows)。在 SQL Server Management Studio打开Tracking_Schema.sql文件。

5.SQL Server Management Studio会在一个新窗口中导入文件中的脚本,但在我们运行脚本前,我们需指明在哪个数据库中运行这些脚本,因此我们要选择WorkflowTracking数据库。

6.点击工具栏上的执行按钮执行这些脚本。

7.重复4-6步执行Tracking_Logic.sql脚本。这将在数据库中创建必须的存储过程。 我们现在就创建了一个将记录跟踪信息的数据库,但怎样得到已记录的信息呢?什么组件进行这方面的工作呢?让我们看看!

使用使用SqlTrackingServer服务 服务

在工作流跟踪数据库设置好后,现在就是实际使用它的时候了。我们先创建一个新的工作流并看看我们怎样去跟踪事件。我们将创建一个稍微复杂一些的工作流,里面有几个事件可以提供给我们去进行跟踪。在我们创建一个原始的工作流后,我们将增加必要的跟踪代码。 创建一个新工作流并进行跟踪创建一个新工作流并进行跟踪 创建一个新工作流并进行跟踪

1. 为更方便些,我已创建了两个版本的样例应用程序。Workflow包含两个不同版本的应用程序:一个是不完全版本,一个是完全版本。完全版本已完全编写完 成并可直接运行,非完全版本可方便你进行修改,并按步骤完成相应练习。你可通过本章后面的下载链接下载这些项目文件。

2.下载本章源代码,打开TrackedWorkflow解决方案,像第三章中相应步骤一样创建一个顺序工作流库的项目,名称为TrackedWorkflow。

3.在你完成以上步骤后,Visual Studio会打开工作流设计器以便进行编辑。

4.从工具箱中拖动一个IfElse活动到设计器界面上。如下图:

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

5.单击左边的ifElseBranchActivity1分支,激活它的属性使其在Visual Studio中的属性窗口中显示。[www.61k.com]

6.寻找 ifElseBranchActivity1 的 Condition 属性。点击下拉列表框上向下的箭头打

wf WF从入门到精通(中文版)

开下拉列表框,选择其中的代码条件节点。如下图:

7.Condition 属性现在会在它的左边呈现出一个“+”号。单击这个+号展开其属性网格,这会暴露出Condition属性的Condition名称字段。在编辑框中,输 入QueryDelay。我们将使用这个方法来决定我们将执行IfElse活动的那个分支。

8.下一步我们在左边的分支(这个分支在条件值为True时执行)添加一些活动。首先,从工具箱中拖拽一个Code活动到IfElse的左边分支即ifElseBranchActivity1上。

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

9. 你看到的惊叹号标记的意思在前面的章节我已描述过,意思是我们还有工作要做。[www.61k.com)在这里,它指出我们需添加一个方法,Code活动添加到工作流中执行时将调用 这个方法。在Visual Studio的属性面板上,定位到 ExecuteCode 属性,在该编辑框中输入

wf WF从入门到精通(中文版)

PreDelayMessage。

10. 也许你要看看我要做什么……其实就是添加一个延时工作流,在第三章我们已经创建过。就像在第三章做的一样,再拖拽一个Delay活动和另一个Code活动 进

ifElseBranchActivity1中,然后设置它们的属性。Delay活动延时10秒(00:00:10),第二个Code活动执行一个名称 为PostDelayMessage的方法。完成这些步骤后的设计器界面如下图所示:

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

11. 在设计器中的工作完成后,我们就来添加相应代码。(www.61k.com)在解决方案资源管理器中的Workflow1.cs文件上单击右键,选择查看代码。然后在项目中添加对 System.Windows.Forms的引用,然后在Workflow1.cs文件的顶部声明和其对应的下面的名称空间。

using System.Windows.Forms;

12. 你查看这个文件,你会看到Visual Studio为你添加的作为活动属性的三个事件处理程序:PreDelayMessage、PostDelayMessage和QueryDelay。和 第三章类似,在Code活动中添加消息对话框,以使应用程序能在工作流执行时通知你。对于PreDelayMessage,添加下面的代码:

MessageBox.Show("Pre-delay code is being executed.");

对于PostDelayMessage,添加下面的代码:

MessageBox.Show("Post-delay code is being executed.");

13.我们些许更感兴趣的是在QueryDelay中添加的以下代码: e.Result = false

wf WF从入门到精通(中文版)

;

wf WF从入门到精通(中文版)

// 假定我们不延时

wf WF从入门到精通(中文版)

// 显示消息

");

} // if

else

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

wf WF从入门到精通(中文版)

{

// 显示消息

");

} // else

14.完成上述步骤后,我们需要为我们的WorkflowTracker主应用程序添加对工作流项目TrackedWorkflow的项目引用,步骤略。(www.61k.com]

15.在WorkflowTracker项目中打开Program.cs文件,查找下面的代码:

Console.WriteLine("Waiting for workflow completion.");

16.为创建一个Workflow实例,在上述代码下添加下面的代码:

// 创建工作流实例。

WorkflowInstance instance =

workflowRuntime.CreateWorkflow(typeof(TrackedWorkflow.Workflow1));

// 启动工作流实例。

instance.Start();

17.编译解决方案,纠正任何编译错误。

18.按下F5(或Ctrl+F5)执行这个应用程序,你将看到以下控制台的输出结果:

我们现在就有了一个基本的工作流,我们可使用它去体验WF的跟踪能力。我们现在就回去添加我们需要的代码以执行跟踪。

为我们的工作流添加为我们的工作流添加SqlTrackingService SqlTrackingService

1.WF由活动和工作流事件 跟踪能力承载,因此我们不需为跟踪事件做太多工作。尽管如此,我们仍然需在主程序文件中添加一些逻辑。首先,要为WorkflowTracker应用程序 添加System.Configuration引用,我们需要它来把访问数据库的连接字符串存储到应用程序的配置文件里。

2.下一步,为 WorkflowTracker应用程序添加一个应用程序配置文件。方法是在Visual Studio的解决方案管理器中的WorkflowTracker树节点上单击右键,依次选择添加、新建项。在呈现的添加新项对话框中选择应用程序配置文 件,点击确定。这就为我们的应用程序添加了一个新的app.config文件。参见下图:

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

3.打开app.config文件,在Configuration的开始标记和结束标记间插入下面的内容: <connectionStrings>

<add name="TrackingDatabase" connectionString="Data Source=(local)\SQLEXPRESS;Initial Catalog=WorkflowTracking;Integrated Security=True;"/>

</connectionStrings>

备注:上面的连接字符串可能和你实际应用中有所不同,你需要灵活进行配置。(www.61k.com]

4.点击WorkflowTracker项目中的WorkflowFactory.cs文件,查看其代码。

5.在该文件中声明以下名称空间(需添加 System.Configuration 引用):

using System.Workflow.Runtime.Tracking;

using System.Configuration;

6. 在WorkflowFactory.cs文件中,找到我们创建WorkflowRuntime实例的地方,在这里我们需要为 WorkflowRuntime引入SqlTrackingService。在GetWorkflowRuntime方法中添加下面的代码:

String conn = ConfigurationManager.ConnectionStrings["TrackingDatabase"].ConnectionString; _workflowRuntime.AddService(new SqlTrackingService(conn));

完成了上述步骤,我们就添加了实际中要去执行跟踪的代码(稍后,我们会添加更多的代码来显示跟踪结果)。编译该解决方案,然后按F5或Ctrl+F5执行它。

备注:假如程序中出现 ArgumentException 异常,最可能的原因是运行时没有访问数据库的权限。

假如工作流运行正常,你可在WorkflowTracking数据库的ActivityInstance表中看到下图5-1中显示的结果。

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

图5-1 表ActivityInstance中的记录

检索来自于工作流的跟踪记录检索来自于工作流的跟踪记录 检索来自于工作流的跟踪记录

1.打开WorkflowTracker项目中的 Program.cs 文件。(www.61k.com)

2.在文件中声明以下名称空间:

using System.Configuration;

using System.Workflow.Runtime.Tracking;

3.在Main方法中,找到下面的代码:

waitHandle.WaitOne();

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

ShowWorkflowTrackingEvents(instance.InstanceId);

ShowActivityTrackingEvents(instance.InstanceId);

5.上面我们调用的一组方法并不存在,我们需要添加它们。在 Program 类中添加这些方法:

Code

在 最后一步中忽然冒出大量的代码,但实际上并不太复杂。我们首先创建了一个

SqlTrackingQuery的实例,为它提供了我们曾提供给 SqlTrackingService的相同的连接字符串。然后我们通过当前工作流实例的ID(一个Guid)标识,从数据库中查询该实例的跟踪信息。该 查询由SqlTrackingService.TryGetWorkflow执行。假如数据库中有我们指定的工作流的跟踪信息,我们循环获取跟踪记录(查 询返回给我们的是一个

workflowTrackingRecord对象的集合),从中提取我们感兴趣的信息。假如查询结果中没有记录,也就没有跟踪信息 写到控制台窗口中。最终的屏幕的输出结果如图5-2所示(在调试

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

模式下运行代码的话,你或许需要设置一个断点才能看到下图的输出结果)。(www.61k.com)

跟踪用户事件跟踪用户事件 跟踪用户事件

SqlTrackingService是WF的一部分,它具有跟踪事件的能力。也就是说,它能跟踪活动和工作流激发的标准事件。但由你生成的事件呢?我们又如何跟踪它们呢?

Activity活动支持一个名叫TrackData的方法,TrackData有两个重载版本:一个版本接受一个要存储进跟踪数据库中的对象,另一个版本接受一个字符串类型的键及一个要存储进跟踪数据库中的对象。

假如你执行TrackData并为跟踪传入通常是字符串类型的数据,那这些信息将作为用户事件数据存入跟踪数据库。

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

检索来自你的工作流的跟踪记录检索来自你的工作流的跟踪记录 检索来自你的工作流的跟踪记录

1.打开WorkflowTracker项目中的Workflow1.cs文件。

2.找到我们在创建工作流时添加的PreDelayMessage方法和PostDelayMessage方法。

3.在名为PreDelayMessage的方法内的显示信息对话框的代码下面添加以下代码: this.TrackData("Delay commencing");

4.同样,在名为PostDelayMessage的方法内的显示信息对话框的代码下面添加以下代码:

this.TrackData("Delay completed");

5.编译并执行。

现在打开WorkflowTracking数据库中的UserEvent表,里面有两行,我们在工作流中每

wf WF从入门到精通(中文版)

调用TrackData一次就产生一条记录,表中部分内容如图5-3

wf WF从入门到精通(中文版)

所示。(www.61k.com]

图5-3 UserEvent表中显示的调用TrackData的结果

创建自定义跟踪配置文件创建自定义跟踪配置文件 创建自定义跟踪配置文件

在本章我已谈到过跟踪配置文件,但当时并未详细深入,在这节我将深入了解它的细节。 你 可回忆一下,跟踪配置文件用来限制WF跟踪架构将存储到跟踪数据库中的信息数量。跟踪配置文件不仅仅是一个XML文档,也用来规定一个给定的工作流的跟踪 将包含和排除的东西。但在代码中完成这些事(比手动添加一个XML的跟踪配置文件)更加容易。这里有一个TrackingProfile对象及在表5-2 中看到的其余对象可用,它们用来创建这个XML文档。 有了这个TrackingProfile对象,你或许会自然的想到这也是一个有用的 XML序列化器,可用来把TrackingProfile对象转换成你需要的XML文档并存入数据库,事实上,这是 TrackingProfileSerializer。WF并不内在支持把XML信息写入数据库,但你可使用类型化的ADO.NET技术及跟踪数据库中提 供的存储过程容易地来完成这一工作。

假如你回去看看表5-2,你会找到一些带有“location”和“point”名称的对象,它们分别和activity、workflow和user事件相对应。我们在这时谈到“location”和“point”究竟意味着什么呢?

在本章我已谈到过跟踪配置文件,但当时并未详细深入,在这节我将深入了解它的细节。 其实,“location”指在你的工作流中活动、工作流或用户相关的事件发生时的一个指定的位置。Locations描述了要跟踪的事件。使用一个location对象,你可更精确地指定你想跟踪及排除的的事件。

跟踪points收集locations,它们能在你的工作流中的一个或多个地方触发跟踪信息。你可把跟踪点当成一个感兴趣的点来考虑。它能在你的工作流代码中跨越不同的位置。在你为跟踪点指定条件和位置后,在跟踪时它视情况可能触发也可能不触发。

为什么谈及所有这些呢?因为当我们建立一个跟踪配置文件时,你真正要做的工作是把跟踪点和位置添加到profile对象中,以作为跟踪事件的过滤器去使用。

创建一个新的跟踪配置文件创建一个新的跟踪配置文件 创建一个新的跟踪配置文件

1.打开WorkflowTracker项目中的Program.cs文件。

2.我们将加入的代码并不一定就难于理解,只是它相当大。首先有必要声明以下一些名称空间: using System.Workflow.ComponentModel;

using System.Data;

using System.Data.SqlClient;

using System.Globalization;

using System.IO; 3.上述步骤后,我们去找到Main方法。在该方法中创建工作流的代码前添加以下代码: TrackingProfile profile = CreateProfile();

StoreProfile(profile, ConfigurationManager.ConnectionStrings["TrackingDatabase"].ConnectionString);

wf WF从入门到精通(中文版)

4.我们在上面的代码中调用了两个方法,我们还需要在Program类中添加它们。(www.61k.com)在Program类中添加CreateProfile方法的代码如下: CreateProfile方法 5.同样,添加StoreProfile方法: StoreProfile方法

6.假如你在现在执行本程序,CreateProfile方法会创建的配置文件并把它写入数据库。

wf WF从入门到精通(中文版)

假如你再仔细地看看步骤4中的代码,你会 注意到仅仅只跟踪了很少数的活动事件和工作流事件。因此,你可能会期望在工作台窗口中将由ShowActivityTrackingEvents和

ShowWorkflowTrackingEvents输出很少的几行信息,但实际上,正确的结果如下图5-4(可把它和图5-2比较)。

图5-4 WorkflowTracker跟踪数据的屏幕输出结果

CreateProfile 方法创建了一个新的TrackingProfile并添加了一个活动跟踪点和工作流跟踪点。每个跟踪点都有一个单一的跟踪位置,它定义了要跟踪哪些事件, 因此我们只能看到来自活动的Executing事件和来自工作流实例的Started事件和Idle事件。

而StoreProfile方法,它把跟踪配置文件序列化成XML形式,然后用典型的ADO.NET技术把这个XML存入跟踪数据库。试图更新一个跟踪配置文件的同一版本会被认为是一种错误,因此会抛出一个异常。

用用WorkflowMonitor查看跟踪信息 查看跟踪信息

假 如有人想出一个现成的用来监测工作流事件的工具那不是很好?就像我们本章前面一样,能把跟踪记录输出是很棒的事,但用一个好的图形用户界面来做这个工作将 是更加棒

wf WF从入门到精通(中文版)

的一件事。(www.61k.com]事实上,我们是幸运的!当我们加载WF时,你也可加载了一套示例,里面包含的是一个叫做WorkflowMonitor的应用程序。在 这里我们需要去做的是编译这个应用程序。 编译编译WorkflowMonitor WorkflowMonitor

1.WorkflowMonitor 是工作流示例库的一部分,它由Windows SDK承载。把WFSample.zip文件复制到本章解决方案的目录下并进行解压。WFSamples.zip文件在下面的位置可找到(声明:本人未在 下面的目录找到该文件,但从微软官方网站上可进行下载,本章的源代码中也提供有WorkflowMonitor的源代码):

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

C:\Program Files\Microsoft SDKs\Windows\v6.0\Samples\WFSamples.zip

2.在Visual Studio中打开WorkflowMonitor.sln文件。

3.编译并生成该应用程序。

这 个应用程序编译时没有错误,然后你却不能执行它。当SqlTrackingService把跟踪记录写进跟踪数据库时,工作流对象的数据类型就是记录的一 批信息中的一条。假如支持你的工作流的类型没有在全局Assembly Cache中,WorkflowMonitor就不能在视图设计器中加载你的工作流对象。因此,对于TrackedWorkflow来说,你必须把你的工 作流组件放到全局Assembly Cache中或者把TrackedWorkflow中的DLL文件放到和WorkflowMonitor的可执行文件(即 WorkflowMonitor.exe)的相同的目录下。在本例中,更容易的方法是复制WorkflowMonitor.exe文件到我们的工作流的可 执行代码的相同目录下。

执行执行WorkflowMonitor WorkflowMonitor

1.复制WorkflowMonitor.exe可执行文件到WorkflowTracker解决方案目录中的bin\Debug子目录下(在此生成的是调试版本的应用程序)。

2.双击WorkflowMonitor.exe文件执行该应用程序。

3.WorkflowMonitor 把配置信息存储在WorkflowMonitor.config配置文件中,可在“Application.LocalUserAppDataPath” 找到。(假如你正运行SQL Server Express,当简单地单击确定,在WorkflowMonitor试图连接该数据库时你可能会看到一条错误信息。)因为这可能是在你的系统上运行 WorkflowMonitor的第一时间,配置文件还未存在。WorkflowMonitor

wf WF从入门到精通(中文版)

已考虑这些并立即显示一个设置对话框。如下图所示:

4.你可通过这个设置来修改跟踪数据库所在的主机名、跟踪数据库名、轮询周期(默认是5秒)等。现在,我们真正要做的是设置服务器的名称和数据库的名称,在你输入这些值后点击确定。

5. 然后WorkflowMonitor监控器建立一个工作数据库的链接,并读出找到的跟踪记录。假如记录中有类型信息,它将在设计器中显示找到的工作流。在 本例中,唯一能找到的工作流是TrackedWorkflow,但在你创建的工作流越多,显示的也将越多。WorkflowMonitor程序

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

的用户界面 如下图所示:

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

第六章 【翻译】WF从入门到精通(第六章):加载和卸载实例 学习完本章,你将掌握:

1.理解工作流实例为什么要卸载和重新加载及其时机

2.理解工作流实例为什么要持久化及其时机

3.搭建SQL Server 2005,使其为WF和工作流持久化提供支持

4.使用SqlWorkflowPersistenceService服务

wf WF从入门到精通(中文版)

5.在你的工作流代码中进行实例的加载和卸载

6.使持久化服务能自动地加载工作流实例及卸载空闲中的工作流实例

假如你花点时间真正考虑该怎样使用WF和工作流在你的应用程序中进行处理的话,你或许想像的许多解决方案中都包含那些需长时间运行的处理过 程。(www.61k.com)毕竟,商业软件本质上就是模拟和执行业务处理过程,这些许多的处理过程中都包含人或厂商、订货和发货、计划安排等等。人没有在几毫秒内自动进行处理的 响应能力,但在已加载的企业服务器上则能做到点。服务器是宝贵、繁忙的资源,需让它进行线程的轮转,让线程等待几分钟、几小时甚至几天、几周是不能接受 的,原因有很多。

因此WF的设计器必须提供一个机制,当等待一些长期运行的任务时,空闲工作流应暂时脱机。WF决定提供 Microsoft SQL Server作为一个可选的存储介质,因为数据库是储存(而不是失去)宝贵数据的好地方。WF也集成了另一个可插拔服务,我们可轻易地把它纳入我们的工作 流中以支持其持久化机制。怎么做到这些、为什么做、什么时候做是我们在本章中将探讨的问题。

持久化工作流实例持久化工作流实例 持久化工作流实例

你知道现代Microsoft Windows操作系统本质上是一个非常特别的批处理软件,它负责为各请求线程分配占用处理器的时间吗?假如一个单一的线程独占了处理器一个过份的时间周 期,其它线程会“饿死”,系统将会死锁。因此这个批处理软件,也就是任务调度程序,要把线程从处理器的执行堆栈上移进和移除,以便所有的线程都能得到执行 时间。

从某个意义上说,工作流也类似。假如你有许许多多长时间运行的工作流,它们都挂到某一特定的计算机上竞争处理时间和资源的话,那么 系统最终会阻塞未处理的工作流。这就没有了可伸缩性。事实上,WF在维护它的工作流队列时是非常高效的,但你可能对这些必须有一个物理上的上限表示赞同, 把空闲、长期运行的工作流从激活执行状态移除是一个好主意。

或者发生什么意外,系统忽然被关闭呢?工作流完全在内存中处理,除非我们采取 步骤持久化它们。因此,除非我们在突发事件发生之前有所准备,否则我们就将丢失了执行中的工作流实例。假如那些长期运行的工作流正管理着关键的进程,它们 丢失了我们能承受得起吗?在大多数情况下,我们都承受不起,至少我们不会心甘情愿地允许这些进程在毫无防备措施的情况下就消失掉。

好消息 是WF不仅为您提供了卸载工作流实例,然后又重新加载的方法,它也支持一个服务:SqlWorkflowPersistenceService,它用来把 工作流实例序列化进SQL Server数据库。假如你读过前面一章,你可能已经熟悉甚至对把工作流实例的信息写入数据库的主张感到满意。

因 此,在什么时侯卸载工作流实例,并且假如他们被卸载,我们应怎么持久化它们呢?在执行中有一些非常特别的点可卸载工作流实例。在大多数情况下,发生这种情 况是在我们会自动为刚才我之所以提到的——WF不能离开(或不愿离开)长期运行的工作流程,在内存中不必要地消耗资源和处理时间的时候。但我们也能亲自进 行控制。这里列出了工作流实例的卸载点,在那些地方可进行持久化。

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

1.在ActivityExecutionContext完成并结束(卸载)后。我们在第四章(活动类型和工作流类型介绍)简要谈过ActivityExecutionContext对象。

2.在Activity进入空闲状态时。

3.一旦一个TransactionScopeActivity完成(卸载)时。我们将在第十五章(工作流和事务)看到这个活动。

4.一旦一个带有PersistOnCloseAttribute属性的Activity完成。

5.当你明确调用WorkflowInstance.Unload或WorkflowInstance.TryUnload时。

wf WF从入门到精通(中文版)

通过调用WorkflowInstance对象的特定方法或通过使用一个Delay活动让你的工作流进入空闲状态,你可控制工作流实例被持久化的时机。(www.61k.com]在延时的时候,通过传递一个参数到持久化服务的构造函数中,你将可对自动的持久化行为进行控制。

备注备注:备注 暂停工作流实例和对工作流实例进行延时是不相同的。使用Delay活动将自动地把工作流实例写入数据库(前提是你正使用 SqlWorkflowPersistenceService并已经对其进行配置,本章的最后一节将看到这些)。暂停仅仅是使工作流从激活的处理状态中撤 出。然而你也可选择使用Unload或TryUnload以手动的方式把工作流写入到数据库中。

WF是如何完成这些的呢?这通过使用 SqlWorkflowPersistenceService并结合创建一个特定的数据库来完成这项任务(这非常像我们在前一章创建的跟踪数据库)。你可 使用相关脚本来创建一个数据库的架构(表和视图)以及执行持久化所需的存储过程。首先让我们来创建数据库。

搭建SQL Server以进行持久化 以进行持久化

就像前一章一样,我们通过在SQL Server Management Studio Express中创建一个新数据库来开始我们的工作。

创建一个SQL Server 2005持久化数据库 持久化数据库

1.启动

wf WF从入门到精通(中文版)

SQL Server Management Studio,连接数据库引擎。

2.在数据库节点上单击右键激活右键快捷菜单,选择“新数据库”。

3.在新数据库对话框中输入“WorkflowStore”作为数据库的名称字段,点击确定。

4. 下一步将执行WF为设置持久化所提供的脚本(这会创建表和视图)。这些脚本的位置在<%WINDIR%>\Microsoft.NET \Framework\v3.0\Windows Workflow

Foundation\SQL\ZH-CHS,在这里<%WINDIR%>是指你的Windows目录(通常是C:\Widows)。在 SQL Server Management Studio打开SqlPersistence.Schema.sql文件。

5.SQL Server Management Studio会在一个新窗口中导入文件中的脚本,但在我们运行脚本前,我们需指明在哪个数据库中运行这些脚本,因此我们要选择WorkflowStore数据库。

6.点击工具栏上的执行按钮执行这些脚本。

wf WF从入门到精通(中文版)

7.重复4-6步执行SqlPersistence.Logic.sql脚本。(www.61k.com]这将在数据库中创建必须的存储过程。

SqlWorkflowPersistenceServiceSqlWorkflowPersistenceService服务介绍 服务介绍

保存和恢复工作流实例是可选的:假如你不想(持久化)的话,你就可避免使用持久化存储介质(如数据库)来保存工作流实例。因此通过可插拔服 务

(SqlWorkflowPersistenceService)来实现持久化或许更有意义。当工作流实例正处于运行中的时 侯,WorkflowInstance和SqlWorkflowPersistenceService协作工作以执行存储和恢复任务。

表面上,所有这些听起来相对地简单。假如我们需要把工作流实例换出并存储到数据库,我们就通知持久化服务为我们存储它。但假如我们使用单一的数据库来持久化不同进程中运行的工作流会发生什么呢?在工作流实例执行中是怎样进行停止和重启的呢?

使 用单一的数据库来存储工作流实例并不罕见。但每个实例可能在不同的机器不同的进程中执行,假如要保存和稍后恢复工作流实例,我们也必须要有一个手段来存储 工作流在执行时刻实际的系统状态。例如,SqlWorkflowPersistenceService会存储实例是否被阻塞(等待其它东西),它的状态 (执行中,空闲等等)以及像序列化实例数据和拥有者标识等各种各样的辅助信息。所有这些信息对于在以后的某个时间重现实例是必须的。

我们能够通过WorkflowInstance对象的三个方法来控制上述的持久化,参看表6-1。 表6-1 WorkflowInstance方法 方法

Load 功能 加载先前被卸载(持久化)的工作流实例

试图从内存中卸载(持久化)该工作流实例。和调用Unload

TryUnload 不同的是,调用TryUnload时假如工作流实例不能立即被卸

载,那它将不会被阻塞(维持执行状态)。

Unload 从内存中卸载(持久化)该工作流实例。注意该方法为进行卸载将阻塞当前执行的线程,直到工作流实例被真正地卸载。

这可以是一个漫长的操作,这取决于个人的工作流任务。

正如表6-1中所指出的,我们有两个方法来用于卸载和持久化工作流实例。你该使用哪个方法取决于你的代码想要做什么事。Unload会暂停工作流 实例来为其持久化做好准备。假如这要花费很长时间,该线程执行Unload操作也就要等待很长时间。然而,TryUnload在请求卸载一个执行中的实例 时将立即返回,但这不能保证该工作流实例真正被卸载并持久化到数据库中。为进行检验,你应检查TryUnload方法的返回值。假如该值是true,该工 作流实例本身就是卸载和持久化了的,假如该值是false,则该工作流实例还没有被卸载和持久化。TryUnload的优点是你的线程不会处在等待状态, 当然缺点是你可能要对该执行中的工作流实例重复地使用TryUnload方法(进行检查)。

卸载实例卸载实例 卸载实例

尽管WF在特定的时间将卸载和持久化你的工作流实例,但有时候你可能想亲自进行控制。对于这些情况,WorkflowInsance.Unload和WorkflowInstance.TryUnload是有助于你的。

假 如你首先没有插入SqlWorkflowPersistenceService就调用上述两个方法中的任何一个的话,WF将抛出一个异常。当然,假如有某 种形式的数据库错误,你也将收到一个异

wf WF从入门到精通(中文版)

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

常。[www.61k.com)因此,好的做法是使用try/catch块来包围这些调用,以阻止你的整个应用程序(发生异常时)崩溃。(注意 这并不是说做任何事都与异常有关,有时你可能想要忽略它。) 我们来试试吧!我们先创建一个小图形用户界面,它为我们提供了一些按钮,我们使用这些按钮来迫使应用程序产生特定的行为。应用程序的复杂性会增加一点,但我们也将走向更加真实的应用中。

这 里我们创建的应用程序仍然相对简单。它仅仅有几个按钮,我们在特定的时间点击它们来迫使工作流实例卸载。(在下一节,我们将重新加载它。)我将故意强制一 个长时间运行的工作流卸载,但和至今我们看到过的工作流不同,它将不会使用Delay活动。这样做的原因就像你或许猜到的一样简单,是因为Delay活动 很特别,它会使自己自动伴随着持久化。相反,我会强制我们的工作流实例卸载而不是像Delay活动那样自动进行卸载。在本章的“在空闲中加载和卸载实例” 这一节我们将看到Delay活动和它们的作用。当前,我们将请求工作流线程休眠(sleep)10秒,以便为我们提供充足的时间来按下我们程序中的按钮中 的一个。 创建一个新的宿主应用程序创建一个新的宿主应用程序 创建一个新的宿主应用程序

1.就像你在前一章做的一样,打开Visual Studio创建一个新应用程序项目。但是,不是要创建一个基于控制台的应用程序,而是创建一个Windows应用程序,名称为

WorkflowPersister。下面的步骤在第二章中已经描述过:包含“添加工作流assembly引用”、“宿主工作流运行时”、“创建 WorkflowRuntime工厂对象”,“启动工作流运行时”,“停止工作流运行时”,“使用工作流运行时工厂对象”,“处理工作流运行时事件”过 程。最后,添加一个app.config文件(可参考前一章中的“添加SqlTrackingService到你的工作流中”,可不要忘记添加 System.Configuration

wf WF从入门到精通(中文版)

的引用)。

2.现在向app.config文件中添加恰当的数据库连接字符串(数据库为WorkflowStore)。 <?xml version="1.0" encoding="utf-8" ?>

<configuration>

<connectionStrings>

<add name="StorageDatabase" connectionString="Data Source=(local)\SQLEXPRESS;Initial Catalog=WorkflowStore;Integrated Security=True;"/>

</connectionStrings>

</configuration>

wf WF从入门到精通(中文版)

3.当你创建了WorkflowPersister项目时,Visual Studio显示了Windows Forms视图设计器。(www.61k.com]在Windows Forms视图设计器中把鼠标移到工具箱上,选择一个Button控件,并把它拖放到设计器的界面上。

4.我们将为这个按钮设置一些富有意义的文字属性,以便于我们知道我们点击的是什么。选中这个按钮,然后在Visual Studio的属性面板中选择该按钮的Text属性,把该属性的值设置为“Start Workflow”。

5.为该按钮添加Click事件的处理程序,具体代码将在后面的步骤中添加。

wf WF从入门到精通(中文版)

6.修改按钮的位置和大小,如下图所示:

7.重复步骤3至步骤5,再添加两个按钮,一个的text属性为“Unload Workflow”,另一个的text

wf WF从入门到精通(中文版)

属性为“Load Workflow”。如下图所示:

8.现在就为测试我们的工作流创建好了用户界面,该是为我们将执行的应用程序添加事件处理代码的时候了。当应用程序加载时我们需要初始化一些东西,做这些工作的一个很合适的地方是在主应用程序窗体中的Load事件处理程序。

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

9.在该事件处理程序(处理方法)中输入下面的代码: _runtime = WorkflowFactory.GetWorkflowRuntime();

_runtime.WorkflowCompleted +=

new EventHandler<WorkflowCompletedEventArgs>(Runtime_WorkflowCompleted);

_runtime.WorkflowTerminated +=

new EventHandler<WorkflowTerminatedEventArgs>(Runtime_WorkflowTerminated);

10。(www.61k.com]在Form1类中声明下面名称为_runtime的字段:

protected WorkflowRuntime _runtime = null;

protected WorkflowInstance _instance = null;

11.添加System.Workflow.Runtime、System.Workflow.ComponentModel和

System.Workflow.Activity三个工作流组件的引用(可参考前面章节),然后在该代码文件中添加下面的命名空间:

using System.Workflow.Runtime;

12.尽管我们现在有了一个应用程序来宿主工作流运行时,但它实际上没做任何事。为完成些功能,我们需向按钮的事件处理中添加一些代码。先向button1_Click中添加下面的代码:

button2.Enabled = true;

button1.Enabled = false;

_instance = _runtime.CreateWorkflow(typeof(PersistedWorkflow.Workflow1));

_instance.Start();

这些代码使“Start Workflow”按钮禁用,而让“Unload Workflow”按钮可用,然后启动了一个新的工作流实例。

13. 下一步,找到“Unload WorkflowInstance”按钮的事件处理:button2_Click,然后添加下面的代码。这里,我们使用 WorkflowInstance.Unload方法来卸载工作流实例并把它写入数据库。在工作流实例卸载后,我们让“Load Workflow”按钮可用。注意假如我们在卸载工作流实例时产生异常,“Load Workflow”按钮是不可使用的。这样做的意义是:假如卸载请求失败,也就不用再加载。

wf WF从入门到精通(中文版)

button2.Enabled = false;

try

{

_instance.Unload();

button3.Enabled = true;

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

} // try

catch (Exception ex)

{

MessageBox.Show(String.Format("Exception while unloading workflow" +

" instance: '{0}'",ex.Message));

} // catch123

备注备注:牢记WorkflowInstance.Unload是同步的,虽然我在本章前面已经阐述过,但 这备注

点很重要。(www.61k.com]这意味着线程试图卸载工作流实例时将会被阻塞(暂停),直到操作完成后为止(不管卸载实例时是成功还是失败)。在这种情况下,可准确执行我想 要的行为(指卸载),因为我不想反复查看实例是否被卸载。但有时,你想在卸载时不会被阻塞,就应使用前面说过的 Workflowinstance.TryUnload。稍后,在你添加完最后一批代码并运行应用程序时,当你单击“Unload Workflow”时密切观察,你会看到应用程序会简短地被冻结,因为它正等待工作流卸载。 14.现在回到我们关注的工作流事件处理 上:Runtime_WorkflowCompleted和Runtime_WorkflowTerminated。这两个事件处理实际上完成相同的动 作,那就是重置应用程序以便为另一个工作流实例的执行做好准备。在“button2”的“click”事件处理方法(该方法包含代码我们已在前面的步骤中 添加)的下面添加下面这些方法: void Runtime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)

{

WorkflowCompleted();

}

void Runtime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)

{

}

15.当然,我们现在还要创建“WorkflowCompleted”方法。假如你熟悉Windows编程,你可能知道Windows一直 以来存在着的限制问题。这个限制简单的说就是你不能在创建窗口控件的线程外的任何线程中改变该窗口控件的状态。因此假如你想改变某个控件的text,你必 须在创建该控件的同一线程上指定该控件的text,使用任何其它线程最有可能导致的结果是造成你的应用程序崩溃。因此我们即将加入的代码对你来说可能很搞 笑,但所有要做的这些的真正目的是确信我们是在原来的创建按钮的线程中来使按钮能用和禁用。(事件处理几乎总是在不同的线程上进行调用。)假如我们只是在 按钮自身的事件处理中使其可用,应用程序可能还是能工作,但它更大的可能是崩溃和挂起。简单地把下面的代码复制并放到源文件Form1类的尾部,应用程序 才会正确的工作。

wf WF从入门到精通(中文版)

private delegate void WorkflowCompletedDelegate();

private void WorkflowCompleted()

{

if (this.InvokeRequired)

{

// Wrong thread, so switch to the UI thread

wf WF从入门到精通(中文版)

3.然后Visual Studio将自动切换到该工作流的源代码文件中,向刚刚插入的PreUnload方法添加下面的代码: _started = DateTime.Now;

System.Diagnostics.Trace.WriteLine(String.Format("*** Workflow {0} started: {1}",

WorkflowInstanceId.ToString(),

_started.ToString("MM/dd/yyyy hh:mm:ss.fff")));

System.Threading.Thread.Sleep(10000); // 10 seconds

wf WF从入门到精通(中文版)

4.为了计算工作流消耗的时间(下面的步骤中我们将看到,该时间至少在两个Code活动执行的时间之间),我在一个名称为“_started”字段中保存了启动时间。[www.61k.com]在你的源文件中构造函数的上面添加该字段:

private DateTime _started = DateTime.MinValue;

5.现在切换到设计视图,添加第二个Code活动。该活动的ExecuteCode属性设置为“PostUnload”,并自动生成该方法。你将看到的设计器界面如下:

6.再次切换回工作流的源代码文件中,向PostUnload方法中添加下面必要的代码: DateTime ended = DateTime.Now;

TimeSpan duration = ended.Subtract(_started);

System.Diagnostics.Trace.WriteLine(

String.Format("*** Workflow {0} completed: {1}, duration: {2}",

WorkflowInstanceId.ToString(),

ended.ToString("MM/dd/yyyy hh:mm:ss.fff"),

duration.ToString()));

7.最后一步是添加一个项目级的引用把该工作流项目引用到我们的主应用程序中,具体步骤可参考前面的章节。

备注备注:但请等等!假如你运行该应用程序,然后点击“Start 备注 现在你或许想运行该程序,

Workflow”按钮而没有点击“Unload Workflow”按钮的话,该应用程序不会出现运行错误。因为一旦工作流被卸载,但我们还没有添加代码来重新加载这个已被持久化(卸载)的工作流实例。 因此在下一节添加代码前,你不应单击“Unload Workflow”按钮。

这样做的目的是在工作流开始执行时,在第一个Code活动 中让它休眠(sleep)10秒钟。在此期间,你可点击“Unload Workflow”按钮来卸载该工作流。在这10秒届满后,该工作流将被卸载并持久化到数据库中。这些事一旦发生,你就可喝杯咖啡、吃吃棒棒糖或者

wf WF从入门到精通(中文版)

其它任 何事休息休息:你的工作流已被保存到数据库中,正等待再次加载。(www.61k.com]让我们看看这又是怎么工作的。

加载实例加载实例 加载实例

WorkflowInstance 公开了二个卸载方法:“Unload”和“TryUnload”,但仅仅只有一个“Load”方法,该方法不用关心工作流实例是怎样存储到数据库中的。一 旦它(工作流实例)被存储,你就可使用WorkflowInstance.Load来把它再次重置到执行状态。现在我们将向 WorkflowPersister应用程序中添加合适的代码来做这些事情。

加载被持久化的工作流加载被持久化的工作流 加载被持久化的工作流

1.在Visual Studio中打开WorkflowPersister应用程序,打开该源代码文件,找到主应用程序窗体,定位到“button3_Click”事件处理方法。

2.在“button3_Click”事件处理中添加下面的代码: button3.Enabled = false; try {

_instance.Load();

} // try

catch (Exception ex)

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

{

" instance: '{0}'", ex.Message));

} // catch

button1.Enabled = true;

现在我们来看看所有这些能否真正工作。我们将运行两次来测试工作流:一次我们直接运行到结束,一次我们将强制其卸载。然后我们比较执行时间并看看SQL Server数据库内记录了些什么。

测试WorkflowPersisiter应用程序 应用程序

1.按下F5键调试WorkflowPersisiter应用程序,如有任何编译错误,请进行修正。注意该测试我们会写一些输出的跟踪信息到“输出”窗口中,因此假如没有“输出”窗口的话,在Visual Studio的“视图”菜单下选择“输出”使其呈现。

2. 点击“Start Workflow”按钮创建并启动一个工作流实例,然后该“Start

Workflow”按钮会被禁用,而“Unload Workflow”按钮将能使用。因为我们让工作流线程休眠10秒钟,经过10秒后,“Unload Workflow”按钮又会自动禁用,“Start Workflow”按钮则重新可用。在本测试中,工作流会一直运行到结束,工作流总共执行的持续时间会是10秒。

3.再一次点击 “Start Workflow”按钮。但是,这次在这10秒的休眠期间将点击

“Unload Workflow”按钮。该按钮在该10秒期间内将会被禁用,过后,“Load Workflow”按钮将能使用。在此时,你的工作流被实例化并保持被卸载状态,直到你重新加载它。

4.但在你重新加载该工作流实例前,请打开WorkflowStore数据库中的InstanceState表,会在该表中看到一行记录,这行就是你的被持久化的工作流实例!

wf WF从入门到精通(中文版)

wf WF从入门到精通(中文版)

5.回到WorkflowPersister程序的执行当中,点击“Load Workflow”按钮。(www.61k.com)“Load Workflow”按钮会被禁用,而“Start Workflow”按钮将能使用。

6.结束WorkflowPersister应用程序。

7.Visual Studio输出窗口中会包含和我们执行的两个工作流有关的信息。

8.在输出窗口中滚动到底部,查找我们注入的文本(我们使用三个星号“***”来装饰这

wf WF从入门到精通(中文版)

些文本)。如下所示:

假 如你回头看看InstanceState表的截图,并把你看到的工作流实例ID和你在Visual Studio输出窗口中看到的工作流实例ID做比较,你会看到在我们的例子中有两个相同的实例ID:bfb4e741-463c-4e85-a9e0- c493508ec4f1。该实例花费的时间为:00:01:41.4859296,第一个工作流实例(ID为:Workflow dab11c11-9534-4097-b5bc-fd4e96cfa66c)花费的时间正如我们期望的,几乎就为10秒钟。两个实例ID和执行时间有区 别,但实质上是一样的。你卸载并持久化到数据库中的工作流实例的运行时间将超过10秒,在InstanceState表中显示的实例ID会和Visual Studio输出窗口中显示的相匹配。

在空闲时加载和卸载实例在空闲时加载和卸载实例 在空闲时加载和卸载实例

我 在本章的前面部分曾经提到,在我们的工作流过程中我们将使用

System.Threading.Thread.Sleep来替换Delay活动,以进行工 作流的测试。在那时我说过,我选择这样做的原因。就持久化而言,Delay活动有一些特殊的处理能力。我们现在就来简要地看看Delay会为我们做些什 么。

假如你在你的工作流中引入了一个Delay活动,那目的明显是要暂停处理一段时间,不管这是一个确切的时间段还是一个不确定的暂停,该暂停会在将来的某个特定时间点,如五天后被取消。

当 执行一个Delay活动时,假如工作流运行时已经附加了

SqlWorkflowPersistenceService服务的话,该工作流运行时将会自动地 为你持久化该工作流,而且当延时周期过期后又自动恢复它。注意,不管运行工作流运行时的系统是否被关闭、重启等等上述事件都会发生。为了使之能自动持久 化,你要在创建你的工作流运行时为SqlWorkflowPersistenceService服务添加一个特定的构造函数参数。(之前的例子省略了这 些,因此工作流不会自动进行持久化。)

我提到的构造函数参数能使SqlWorkflowPersistenceService的 internal方法

wf WF从入门到精通(中文版)

(www.61k.com)

扩展:wf从入门到精通 pdf / wf从入门到精通 / wf 入门

本文标题:c从入门到精通完整版-麓山文化《中文版会声会影X7从入门到精通》
本文地址: http://www.61k.com/1138430.html

61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1