自动售货机用户界面模板的c#形式用什么模板?

workFlow从入门到精通_学霸学习网
workFlow从入门到精通
【翻译】WF 从入门到精通(第一章):WF 简介以下是一小段进行邮政编码验证的代码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 组件,我们将对其进行修改以进行邮政编码的 验证,第一次使用你就可以放入许多东西,但我们现在还是从使用这个简单的 Workflo w 开始工作吧。 创建一个工作流 在工具箱中拖一个 IfElse 活动组件到 workflow 设计界面上。我们现在将使用 IfElse 活动组件来问下面的问题:“我们已有的一个字符串是一 个合法的邮政编码吗?”。我们在代码中将确实使用先前你看到的代码段中的正则表达 式来问这一问题。 但在我们进行这一步前请仔细看看 workflow 的视图设计器。workflow 视图设计器 提醒我们没有提供相应的代码来做这一决定。假如你看看命名为“ifElseBranchActivi ty1”的左边分支的右上角,你将看到里面有一个惊叹号标记的小圆形图标。这是 work flow 视图设计器在告诉你 workflow 并不完整。假如你试图编译带有这种提醒图标的项 目时,将会编译报错。如你把鼠标移到图标上并单击呈现出的向下箭头时,你还能看到 Edited by Foxit Reader Copyright(C) by Foxit Software Company, For Evaluation Only.更多关于这一错误情况的信息。在 IfElse 活动的分支上添加代码 1.移动鼠标到命名为“ifElseBranchActivity1”的左边分支上,单击以在属性面 板上激活这一分支的属性。 2.我们添加一个条件, 意思是说 workflow 执行到分支时将强制其执行一些动作 (条 件判断为 True 时,将执行左边分支)。为做到这些,单击“condition”属性激活条件 类型属性的下拉列表。从列表中你可以选择“代码条件”类型、“(无)”类型和“声 明性规则条件”类型。这里选择“代码条件”类型。 3.上述步骤完成后,“condition”类型属性用户界面会包含一个“+”号,我们单 击展开它的子属性,该子属性也命名为“condition”,单击以激活它。 4.“condition”属性需要输入我们想添加的内部事件名字。当条件需要判断时这 个事件将激发。在本例子中我们输入“EvaluatePostalCode”。 Edited by Foxit Reader Copyright(C) by Foxit Software Company, For Evaluation Only.Visual studio 2008 在幕后为你在 workflow 源文件中添加了你在 “condition” 属 性中所指明的事件。稍候我们将添加在事件激发时所要执行的正则表达式代码段。 在我们做这些工作之前,让我们继续在 workflow 视图设计器上工作,我们刚刚增 加了一个条件,它将引发工作流选择左边路径还是右边路径。但是两条路径中都没有指 明工作流将进行的动作。我们在左边“ifElseBranchActivity1”分支和右边“ifElseB ranchActivity2”分支中添加活动。 添加 Code 活动 1.拖一个“Code”活动到 workflow 视图设计器上,并放到左边分支(ifElseBranc hActivity1)的区域内。 2.就像先前添加条件判断的代码一样,我们将为该分支添加被选中时执行的代码。 单击“codeActivity1”图标以在属性面板中激活它的属性。 3.在“ExecuteCode”属性中输入“PostalCodeValid”。 Edited by Foxit Reader Copyright(C) by Foxit Software Company, For Evaluation Only.Visual Studio 2008 会自动插入该事件。 稍候我们会提供对应的执行代码。 右边分 支也同样做,只是要在“ExecuteCode”属性中输入“PostalCodeInValid”。 在我们的 workflow 中添加事件处理代码 1.打开 Workflow.cs 准备进行编辑 2.添加引用:using System.Text.RegularE 3.定位到“EvaluatePostalCode”方法上,插入下面的代码:private void EvaluatePostalCode(object sender, ConditionalEventArgs e) { string USCode = @&^(\d{5}$)|(\d{5}$\-\d{4}$)&; string CanadianCode = @&[ABCEGHJKLMNPRSTVXY]\D[A-Z]\d[A-Z]\d&; atch(_code, USCode) || Regex.IsMatch(_code, CanadianCode)); e.Result = (Regex.IsM}变量 e 是“ConditionalEventArgs”类型的实例,它用来告知“IfElse”活动应选 择哪条路径。 4.我们也需要为 workflow 活动添加一种能力,以便接受输入的字符串来进行验证 工作。为此我们添加下面的代码,我们将声明一个名为“PostalCode”的公有属性。 private string _code=string.E public string PostalCode { get { return _ } set { _code = } }有了这些,我们的 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(PCode Flow.Workflow1),wfArgs); 3.把上述代码替换为:Dictionary&string, object& wfArgs = new Dictionary&string, object&(); wfArgs.Add(&PostalCode&, args.Length & 0 ? args[0] : &&); eWorkflow(typeof(PCodeFlow.Workflow1),wfArgs); WorkflowInstance instance = workflowRuntime.Creat 编译应用程序 执行你的工作流应用程序 1.打开命令提示符窗口。 2.在命令提示符下定位到编译后所生成的应用程序目录。 3.输入 pcodeflow 12345 查看执行结果。 4.输入 pcodeflow 1234x 再看看执行结果。 源码下载:PCodeFlow Edited by Foxit Reader Copyright(C) by Foxit Software Company, For Evaluation Only.【翻译】WF 从入门到精通(第二章):workflow 运行时上一篇:【翻译】WF 从入门到精通(第一章):WF 简介学习完本章后,你将掌握: 1.在你的应用程序中使用 workflow 2.理解“WorkflowRuntime”对象的的基本功能 3.知道如何启动和停止 workflow 运行时 4.能够使用各种 workflow 运行时的相关事件当你在 WF 环境中执行任务时,需要一些东西来监管执行的过程,这个东西就是命 名为“WorkflowRuntime”的对象。WorkflowRuntime 启动一个独立的工作流任务。在你 的任务执行过程中,WorkflowRuntime 也会针对不同的情况响应对应的事件。并且,Wo rkflowRuntime 还能在你的执行环境中增加一个附加的服务来保持跟踪。 WF 架构纵览见下图: WF 和你的应用程序并行执行。其实,我们需要你的应用程序作为宿主。宿主应用程 序可以是 Windows Forms 应用程序,控制台应用程序,ASP.NET WEB 应用程序,甚至可 以是一个 Windows Server。WF 运行时和你的应用程序同在一个.NET 应用程序域执行, 每个应用程序域只有一个唯一的 WorkflowRuntime 实例, 试图在一个应用程序域中建立 第二个 WorkflowRuntime 的实例的话,其结果就是抛出一个“InvalidOperationExcept ion”异常。workflow 应用程序-“workflows”-意思指创建的逻辑上的一组活动。这些逻辑上 的活动用来完成你需要的工作流任务。当你宿主 workflow 运行时的时候,其实你就在 操作工作流中的活动并让 workflow 运行时执行他们。 其结果就是生成一个 workflow 实 例,workflow 实例是一个当前正执行的 workflow 任务,它能自己完成逻辑上的一组活 动,回忆第一章吧,活动能执行你提供的代码并且能对输入的数据做出相应的决定。下 一章我们将简述工作流实例,后面几章将对活动进行介绍。在宿主应用程序中添加 WF 一、创建一个名称为 WorkflowHost 的控制台应用程序项目 二、为项目添加名为 System.Workflow.Runtime 的引用 三、宿主 workflow 运行时 1.打开 Program.cs 文件准备编辑 2.在“using System.T”下添加以下代码: “using System.Workflow.Runtime” 3.定位到“Main”方法,在里面添加以下代码: WorkflowRuntime workflowRuntime=new WorkflowRuntime(); 4.编译程序确认没有错误。在本章我们都将使用这一应用程序。 四、深入了解 WorkflowRuntime 对象 我们现在已经在我们的宿主应用程序中建立了一个 WorkflowRuntime 类型的实例, 该是简单的了解怎样和这个对象交互的时候了。和大多数有用的对象一样,WorkflowRu ntime 也暴露了一些方法和属性,我们可用他们来控制 Workflow 运行时的环境。表 2-1 列出了所有 WorkflowRuntime 属性,表 2-2 则列出了我们经常使用的方法。 表 2-1 WorkflowRuntime 的属性属性 功能用来指明 workflow 运行时是否已经启动并准备接受 workflow 实例。当宿主调用“StartRuntime”前 IsStarted 为 F IsStarted alse。期间它一直维持 True 直到宿主调用“StopRuntime”为止。需注意的是当它正在运行中你不能增加核心服务。获取或设置和 WorkflowRuntime 关联的名字。Workflow 运行时正在运行中你不能设置这个属性(也就是说当 IsStart Name ed 为 True)。企图这样做的结果就是抛出一个“InvalidOperationException”异常。表 2-2 WorkflowRuntime 的方法方法 功能AddService为 workflow 运行时添加指定的服务。能添加的服务类型和时间受到种种限制。关于服务的详细信息将在第五章介绍。创建一个 workflow 实例,它包含一些指定(但可选)的参数。假如 workflow 运行时没有启动,该方法就调用 Start CreateWorkflow Runtime 方法。通过 指明 workflow 实例的标识符(由一个 Guid 组成)来检索 workflow 实例。假如这个 workflow 实例是空闲和持 GetWorkflow 久化保存的,它将被重新加载并执行。 StartRuntime启动 workflow 运行时和相关服务,并引发“Started”事件。StopRuntime停止 workflow 运行时和相关服务,并引发“Stoped”事件。还有更多的关于 WorkflowRuntime 的方法, 但表 2-2 中列出的方法是最经常用到的 方法,也是我们将重点关注的方法。在 workflow 运行期间,WorkflowRuntime 也将在各 种时间引发许多事件,但我们将在后面的章节中介绍。 创建一个 Workflow 运行时工厂 单例和工厂设计模式的组合是强大的, 因为工厂能保证只创建出一个曾创建的对象 的单一实例, 这正好符合我们的要求 (在这里使用单例模式的原因主要是从效率上考虑, 其次一个应用程序域也只能只有一个 WorkflowRuntime),因为 WorkflowRuntime 完全 有可能在不同的应用当中加载和启动(例如独立的应用模块)。让我们看看怎样创建一 个 WorkflowRuntime 工厂。 一、在项目中添加一个类型为类的新项,文件名为 WorkflowFactory.cs。 二、在 WorkflowFactory.cs 源文件中添加如下的引用 using System.Workflow.R 三、在类中添加下面的代码://workflow runtime 的单一实例 private static WorkflowRuntime _workflowRuntime = private static object _syncRoot = new object();四、在上述代码后添加如下方法: //工厂方法 public static WorkflowRuntime GetWorkflowRuntime() { //多线程环境下防止并发访问 lock (_syncRoot) { if (null == _workflowRuntime) _workflowRuntime = new WorkflowRuntime(); } return _workflowR }五、为类加上 Public 关键字,为防止类被直接实例化,还必须为类加上 static 标 记,如下所示: public static class workflowFactory启动 workflow 运行时参考表 2-2, 里面有一个 StartRuntime 方法, 从我们的工厂对象中调用这个方法很 有意义, 外部对象要求 workflow 运行时对象无需处理或担心运行时环境状态的初始化。 我们需要在我们的应用程序通过这一步来建立我们需要的 workflow 环境。外部调用对 象也需要 workflow 运行时对象易于使用。 并不是一定要调用 StartRuntime。假如我们建立了一个 workflow 实例,StartRun time 实际上就已被调用。假如我们曾经创建了一个 workflow 实例,或许并不用担心需 要明确的调用 StartRuntime。但是,一旦我们添加服务时,明确地调用它就很有必要, 因为可增强代码的可维护性并确信运行时环境的状态已建立,这样任何人就都能使用 w orkflow 运行时对象。 因此让我们在我们的工厂对象中做些轻微的更改并直接调用 StartRuntime。 1.打开 WorkflowFactory.cs 文件并定位到下面的代码上: _workflowRuntime = new WorkflowRuntime(); 2.在上面的代码下添加以下的代码: _workflowRuntime.Starttime();停止 workflow 运行时是否有办法启动一个 workflow 运行时很有意义, 如何停止一个 workflow 运行时也 一样。看看表 2-2 吧,里面有一个 StopRuntime 方法正好符合我们要求。调用 StopRun time 方法会卸载所有正执行的 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 { _workflowRuntime.StopRuntime(); } catch (ObjectDisposedException) { } } } }以下是 WorkflowFactory.cs 文件的完整源代码,在第五章之前我们不会做更多的 改变:using S using System.Collections.G using System.L using System.T using System.Workflow.Rnamespace WorkflowHost { public static class WorkflowFactory { //workflow runtime 的单一实例 private static WorkflowRuntime _workflowRuntime = private static object _syncRoot = new object(); //工厂方法 public static WorkflowRuntime GetWorkflowRuntime() { //多线程环境下防止并发访问 lock (_syncRoot) { if (null == _workflowRuntime) { AppDomain.CurrentDomain.ProcessExit += new EventHandler(StopWorkflowRuntime); AppDomain.CurrentDomain.DomainUnload += new EventHandler(StopWorkflowRuntime); _workflowRuntime = new WorkflowRuntime(); _workflowRuntime.StartRuntime(); } } return _workflowR } 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 运行时停止后激发。WorkflowComplete当一个 workflow 实例完成后激发。d当一个 workflow 实例进入空闲状态时激发。当 workflow 实例进入了空闲状态后,你 Workflow 就有机会把他们从内存中卸载掉、存储到数据库并可在稍后的时间把它们加载进内 Idled 存。Workflow 当一个 workflow 实例被终止后激发。在宿主中调用一个 workflow 实例的 TerminateTerminate 方法、或通过一个 Terminate 活动、或当 workflow 运行时产生一个未经捕获的异常时d都会终止该 workflow。我们还将在第四章和第五章介绍更多的事件。在我们为上面的事件添加相应的事件处理程序时, 你会看到生成的代码和上一章我 们创建的基于工作台的顺序工作流应用程序中的代码完全一样(或几乎完全一样)。为 了看看这些事件的作用,我们需要停止应用程序主线程一段时间。因此,我们使用一个 基于内核的自动重置事件。一会儿后,我们将写出一些代码来使用上述事件中的几个, 你需要不时看看第一章中 PCodeFlow 项目中的 Program.cs 文件,对比它们的不同以及 该写入什么样的代码。尽管它们并不完全相同,但你在两个程序中还是能找到相同的内 容。处理 workflow 运行时事件 1.启动 Visual Studio,打开项目的 Program.cs 源文件,定位到下面的代码上: WorkflowRuntime workflowRuntiem=WorkflowFactory.GetWorkflowRuntime(); 2.假如你用过.NET 的委托, 下面的代码你将非常熟悉。 我们需要为我们感兴趣的事 件增加相应的事件处理程序。我们现在就来为 workflow 空闲时和完成后增加相应的事 件处理程序。稍候我们还会增加我们所需要的更多的事件处理程序。记住,下面的代码 在步骤 1 定位的代码的下面: workflowRuntime.WorkflowIdled += new EventHandler&WorkflowEventArgs&(wo rkflowIdled); 3.下面的代码添加了对 workflow 完成后的事件处理: workflowRuntime.WorkflowCompleted += new EventHandler&WorkflowCompleted EventArgs&(workflowCompleted); 4.现在添加对 workflow 终止后的事件处理: workflowRuntime.WorkflowTerminated += new EventHandler&WorkflowTerminat edEventArgs&(workflowTerminated); 5.假如你编译并运行 WorkflowHost(本项目),这个应用程序能通过编译并运行。 但没有执行 workflow, 因为我们并未告知 workflow 运行时去启动一个 workflow 实例 (我 们将在下章添加)。为以后做准备,我们还要添加一些代码。首先,为了激发 workflo w 中的事件(以便我们观察它们),我们需要停止主线程足够长的时间,因此我们还将 添加自动重置事件。在步骤 3、4 的代码下添加以下代码。Console.WriteLine(&对待 workflow 完成。&); waitHandle.WaitOne(); Console.WriteLine(&完成.&); 6.在 Main 方法前定义一个名为 waitHandle 的静态成员: private static AutoResetEvent waitHandle = new AutoResetEvent(false); 7.添加名称空间: using System.T 8.由 Vistual Studio 2008 创建的上面三个事件对应的事件处理程序内都包含“t hrow new NotImplementedException();”。我们需要移除这些代码并定位到 wor kflowIdled 的事件处理程序内,写入下面的代码: Console.WriteLine(&workflow 实例空闲中&); 9.定位到 workflowCompleted 的事件处理程序内,写入下面的代码: Console.WriteLine(&workflow 实例已完成&); waitHandle.Set(); 10.定位到 workflowTerminated 的事件处理程序内,写入下面的代码: Console.WriteLine(&workflow 实例已终止,原因:'{0}'。&,e.Exception.Mess age); waitHandle.Set();完整的代码见列表 2-2。 列表 2-2 WorkflowHost 应用程序的完整代码using S using System.Collections.G using System.L using System.T using System.Workflow.R using System.Tnamespace WorkflowHost { class Program { private static AutoResetEvent waitHandle = new AutoResetEvent(false); static void Main(string[] args) { WorkflowRuntime workflowRuntime = WorkflowFactory.GetWorkflowRuntime(); workflowRuntime.WorkflowIdled += new EventHandler&WorkflowEventArgs&(workflowRuntime_Workflow Idled);workflowRuntime.WorkflowCompleted += new EventHandler&WorkflowCompletedEventArgs&(workflowRu ntime_WorkflowCompleted);workflowRuntime.WorkflowTerminated += new EventHandler&WorkflowTerminatedEventArgs&(workflowR untime_WorkflowTerminated);Console.WriteLine(&等待 workflow 完成。&); waitHandle.WaitOne(); Console.WriteLine(&完成.&); }static void workflowRuntime_WorkflowIdled(object sender, WorkflowEventArgs e) { Console.WriteLine(&workflow 实例空闲中&); }static void workflowRuntime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e) { Console.WriteLine(&workflow 实例已完成&); waitHandle.Set(); } static void workflowRuntime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e) { Console.WriteLine(&workflow 实例已终止,原因:'{0}'。&,e.Exception.Message); waitHandle.Set(); } } }下一章我们将深入 workflow 实例,假如现在你执行这个程序,他会一直挂起。为 什么呢?因为我们从未执行一个 workflow 实例,因此我们加入的事件的从未被激发, 也就未执行对应的事件处理程序。程序将永远挂起(或者你亲自终止它)。在下一章中 当我们添加一个 workflow 实例并执行它时我们还会看到这个程序。 源码下载:WorkflowHost 【翻译】WF 从入门到精通(第三章):workflow 实例上一篇:【翻译】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 去执行的东西,而一个 workflo w 实例是一个正在执行的“workflow definition”。它们之间有明显的区别,一个正在 执行当中,而另一个则不是。 workflow 实例从哪里来?它们当然应由你来创建。 如你有困难来完成这个任务, 并 且自动创建的 workflow 符合你的应用要求的话,也可由软件来完成,但至少你也要写 出 workflow 的任务或者 workflow 运行时将为你执行的任务。 Microsoft 提供了 workfl ow 运行时,你还得创建余下的东西。毕竟,这是你的应用。 WF 在上述这些地方的创建上可以为你提供帮助,WF 不仅将执行你创建的 workflow 实例,而且也将帮助你去创建它们。WF 集成了丰富的图形界面设计器,它能帮你以相同 的方式把 workflow 集成到你创建的 ASP.NET Web Forms、 Windows Forms 或者 WPF 应用 中。你可在工具箱上滚动鼠标,从许多活动项中选中一个,然后把它拖到设计界面上并 释放它。假如这个活动项具有可配置的属性,你还可使用 Visual Studio 中的属性面板 来配置它,使它符合你的意图。我们已在第一章简要地使用过 workflow 设计器,在这 里我们将再次使用它,毕竟与 WF 相关的工作几乎全是创建 workflow 任务,workflow 可视化设计器的使用是开发过程中巨大的一个组成部分。 workflow 实例和任何其它软件类似。它们会开始执行、运行,直到遇到终止条件时 终止。 这些或许是数据库中的所有记录已被处理, 所有需被压缩的档案已被压缩,或者 w orkflow 发向各个审批方的文档已被批复(同意或不同意),或者是处理已经完成。它 只有一个正常的启动位置,但有一个或多个正常的可能停止的位置。 实例也能维持错误、异常。你可以处理这些异常也可不处理它。在某些情况下,或 许你不想去处理出现的异常,并留到以后进行处理。 有时,一个 workflow 处理过程会执行很长很长时间才能完成。例如,一个处理过 程发送了一份零件的订单并等待订单被接收。在 workflow 终止前须确认零件的型号和 数目,而这或许会花去几天,几周甚至几月。因此,难道一个 workflow 实例也需要在 内存里维持激活状态几天, 几周或者几月吗?假如服务器崩溃或电源断电怎么办?你的 w orkflow 实例、数据、应用程序状态不是通通丢失了吗? workflow 实例和组成实例的活动是 workflow 处理过程中的重要部分。WF 已经为 w orkflow 实例的创建及执行提供了强大的支持。我们就来看看 WorkflowInstance 对象。 WorkflowInstance 对象介绍 workflowInstance 是一个 WF 对象,它为你提供了你的独立的 workflow 任务上下 文(环境)。你可使用这个对象去找到在你的处理任务中事情将是如何进行的。就像我 们有方法和属性去控制 workflow 运行时一样, 我们也有方法和属性并用它们和我们的 w orkflow 实例进行交互。 3-1 列出了大多数 WorkflowInstance 属性, 3-2 列出了经 表 表 常使用的方法。我们还将在第五章看到一些额外的属性和方法,“工作流跟踪”。 表 3-1 WorkflowInstance 的属性属性 功能InstanceId得到 workflow 实例的唯一标识(一个 Guid)WorkflowRuntime得到本 workflow 实例的 WorkflowRuntime表 3-2 WorkflowInstance 的方法方法 功能 通过 WorkflowChanges 对象申请对 workflow 实例进行更改。这允许你在 workflow 执行时修改它(增加、移出或更改 ApplyWorkflowChanges 活动),当动态的更改实施时,workflow 实例会被暂停。GetWorkflowDefinition检索本 workflow 实例的根(root)活动。恢复执行先前被暂停的 workflow 实例。假如 workflow 实例并未处于暂停状态,则不做任何事情。假如 workflow 实 Resume 例处在暂停状态,workflow 运行时就会在 workflow 实例刚被恢复后触发 WorkflowResumed 事件。启动一个 workflow 实例的执行,在这个 workflow 实例根活动上调用 ExecuteActivity。假如 Start 发生异常,它通 Start 过调用 Terminate 终止这个 workflow 实例,并附加异常相关信息作为终止的原因。同步暂停本 workflow 实例。假如 workflow 实例本就处于暂停状态,则不做任何事情。假如 workflow 实例正在运行,Suspend则 workflow 运行时就暂停该实例,然后设置 SuspendOrTerminateInfoProperty(说明原因)并进入 Suspend,触发 WorkflowSuspended 事件。同步终止本 workflow 实例。当宿主需要终止 workflow 实例时,workflow 运行时就终止这个实例并试图持久化实例的最终状态。然后 WorkflowInstance 设置 SuspendOrTerminateInfoProperty(说明原因)并进入 Terminate。最后,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 活动和一个 D elay 活动。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 St udio 属性面板中改变 TimeoutDuration 的属性。把最后两个“00”改为“10”,意思是 Delay 活动将等待 10 秒钟才允许 workflow 继续下一步的处理。 9.现在我们需要添加第二个 Code 活动来显示第二个信息对话框。 为此, 重复步骤 2 和步骤 6 添加一个新的 Code 活动,但设置 ExecuteCode 的属性为“PostDelayMessage& 来作为事件的命名。以下是在 workflow 视图设计器中展示的 workflow 的最终结果:我们还剩下两个任务未完成。最终,我们需要把我们的 workflow 程序集引入到我 们的主应用程序中以便执行它。但首先,我们必须添加必要的代码以显示那两个信息对 话框。我们已经在我们的 workflow 代码创建了两个事件:PreDelayMessage 和 PostDel ayMessage。我们将为它们添加事件处理代码,在里面实际上就是弹出信息对话框的代 码。 为延时前和延时后的事件添加代码 1.单击 LongRunningWorkflow 项目中的 Workflow1.cs 文件,查看其代码。 2.添加“System.Windows.Forms&的引用,并在 Workflow1.cs 文件声明以下名称空 间: using System.Windows.F 3.定位到新插入的 PreDelayMessage 方法,在方法中插入以下代码: MessageBox.Show(&正在执行延时前的代码。&); 4.和上一步类似,定位到新插入的 PostDelayMessage 方法,在方法中插入以下代 码: MessageBox.Show(&正在执行延时后的代码。&); 假如你这时编译这个解决方案,那没有任何错误,但是 WorkflowHost 应用程序仍 旧会像先前一章一样挂起。为什么呢?因为尽管我们创建了一个我们能够使用的 workf low 程序集,但我们并未请求主应用程序去执行它。WorkflowCompleted 事件从未被触 发,因此自动重置事件也就不会释放应用程序主线程。 为执行我们的 workflow 任务, 我们要引用我们新创建的 workflow 程序集并添加代 码,以使 WorkflowRuntime 对象来揭开 workflow 任务工作的序幕。我们现在就开始吧。 宿主一个自定义 workflow 程序集并启动一个不带参数的 workflow 实例 1.首先在项目 WorkflowHost 中添加对项目 LongRunningWorkflow 的引用。 2.假如我们现在去编译这个应用程序, WorkflowHost 将编译失败。 为什么呢?原因 是在前一章我们创建 WorkflowHost 项目时,我们仅仅添加了必须的引用以支持当时的 环境编译通过。但现在,我们添加了一个“System.Workflow.Runtime”引用。同时又 引入了一个现成的 workflow 程序集到我们的宿主应用程序中,因此我们需要为 Workflo wHost 项目添加更多的和 workflow 相关的引用。我们需要添加的引用有“System.Work flow.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 序列化和.NE T 中的“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 _ } set { if (value & 0 || value & 120) value = 10; if (ExecutionStatus == ActivityExecutionStatus.Initialized) { _delay = delayActivity1.TimeoutDuration = new TimeSpan(0, 0, _delay); } } } 我们对传入的整形值进行了检查,假如它超出范围,我们就指定一个默认值。我们 检查了 workflow 是否处在即将执行状态(不是已执行状态)。这可防止有人在我们的 w orkflow 运行中对延时值进行修改。 我们也需要对 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(&你必须输入一个整形值!&); }Dictionary&string, object& parms = new Dictionary&string, object&();4.找到下面的代码:WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1)); 5.把上述代码改为:WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(LongRunningWorkflow.Workflow1), parm s);编译并像在第一章那样运行试试,通过输入不同数值的延时时间,就可看到弹出的 两个信息对话框所反映出的延时效果。 确定 Workflow 实例的状态 有趣的是,假如你看看 workflow 运行时对象及 workflow 实例对象的方法和属性, 你找不到和状态相关的属性。 你怎么知道是否有一个 workflow 在执行呢?假如有一个, 它处在那个状态呢?空闲吗?正执行当中吗?我们怎么确定? 我将向前跳一小段,这其中大部分逻辑都放在 workflow 状态的确定上。一个给定 的 workflow 实例的 workflow definition 为您提供 workflow 的执行状态。基类 Activ ity 暴露了一个 ExecutionStatus 属性,它是一个 ActivityExecutionStatus 枚举的一 个成员。下表 3-3 列出了 ActivityExecutionStatus 的枚举值和相关的意义。 表 3-3 ActivityExecutionStatus 枚举值属性 功能Canceling活动正在取消中。Closed活动已被关闭。Compensating 活动处于补偿状态。Executing活动当前正在运行。Faulting活动已产生并维持一个异常。Initialized活动已被初始化但还未运行。表 3-3 中的所有枚举值都涉及到一个活动对象,但你需记住 workflow definition 就是一个活动。这意味着假如我们查询 workflow definition 的状态,我们就能有效地 确定整个实例的状态。下面的过程演示了我们怎样添加相应代码来查询 workflow defi nitely。 确定 workflow 实例执行状态 1.打开 WorkflowHost 项目的 Program.cs 文件准备编辑。 2.找到 Main 方法并定位到下面的代码上: instance.Start(); 3.为了让我们看到 workflow 实例的状态,我们直接查询 workflow definition 的 状态并把结果输出到控制台中显示出来。在上一步中定位到的代码下插入以下代码: Console.WriteLine(&workflow 处在:{0}状态。&, instance.GetWorkflowDefiniton().ExecutionStatus.ToString()); 终止 Workflow 实例 假如你需要这样做的话, 你也能容易地终止一个 workflow 实例, 方法是通过执行 w orkflow 实例对象的 Terminate 方法。 假如你在你的应用中添加了 WorkflowTerminated 的事件处理,你就能从 Exception 的 Message 属性获取终止的原因。你将发现 Excepti on 被包装到 WorkflowTerminatedEventArgs 中,并传入到 WorkflowTerminated 的事件 处理程序中。这些代码 WorkflowHost 中已经包含了,我们还需添加一行代码来结束 wo rkflow 实例。 终止 workflow 实例 1.打开 Program.cs 文件,找到如下我们刚添加的代码上: Console.WriteLine(&workflow 处在:{0}状态。&, instance.GetWorkflowDefinition().ExecutionStatus.ToString(); 2.在上述代码下添加以下代码: instance.Terminate(&用户取消&); 假如你现在编译并运行 WorkflowHost 程序,为他提供一个 25 秒的延时值,你不会 再看到任何一个信息对话框,控制台的输出结果如下: Dehydration 和 Rehydration 在我们离开 workflow 实例的这一话题之前,我想再谈谈“dehydrating”和“rehy drating”一个实例的概念。假如你有一个长时间运行的 workflow 任务或者有大量的任 务执行,你就能卸载任务并把必须的执行环境信息存储到一个 SQL Server 数据库中, 这要用到运行在 WF 之上的一个服务。 我们将在第六章详细讨论存储的问题,我在这提及它是因为,对一件事来说,处理 的目标是 workflow 实例。但另一方面,我们应听听这些术语,我不想让你在深入此书 后却还不理解它们的基本意思。 当你“dehydrate”一个实例时,你就正在把它从执行状态中移除并进行存储以便 以后恢复。典型的做法是使用 WF 的持久化服务,但你也能写你自己的服务来做同样的 任务。以后当你的应用程序侦测到需重启 workflow 实例时,你就“rehydrate”这个实 例它就返回当时的执行状态。这样做的原因有很多,所有这些本书稍后都会简要说明。 源码下载:WorkflowHost 【翻译】WF 从入门到精通(第四章):活动及 workflow 类型介绍上一篇:【翻译】WF 从入门到精通(第三章):workflow 实例学习完本章,你将掌握: 1.workflow 活动是怎样形成的 2.顺序工作流和状态机工作流之间的区别 3.创建一个顺序工作流 4.创建一个状态机工作流活动是 WF 中 workflow 处理的基本单位,假如你再把一个业务处理过程(或 workf low 任务)进行分解,你会发现它由更小、更细的任务组成。假如需设计这样一个大的 任务,它需把信息送到一系列的数据处理系统进行处理,那么子任务或许就包括这样一 些事情:从数据库读取数据,使用这些数据生成一个文件,通过使用 FTP 或 XML Web s ervice 把文件传到一个远程服务器上, 标记信息已经被处理 (如通过写入数据库并进入 审计步骤),等等。这些子任务都聚焦到一个特定的任务上:读数据库、上传文件、进 行审计。换句话说,它们是活动。 当你创建 workflow 时,你会把这些单独的活动捆在一起,并让活动从一个转到另 一个。一些活动可作为其它活动的容器。一些活动执行一个单一任务,这我们已谈过。 基于容器的活动用来容纳其它活动,在前一章中我们谈及的 root 活动就是这种活动。r oot 活动既可是一个顺序活动也可是一个状态机活动,本章中我们将对这些活动的类型 进行说明。 活动怎样知道在本步骤完成后下一步将做什么呢?本章将主要把焦点放在这上面 上。或许活动会以你创建一个 root 活动时指定的顺序执行,或者可能是仅在一个特定 的事件发生后才去执行一个指定的活动。为了让我们更好地理解活动,我们首先要去看 看 WF Activity 对象,然后看看活动是怎样链接在一起的。 活动介绍:基本的工作单位 WF 为你提供了一个 Activity 对象。Activity 实现了一个看起来很简单的基类。它 不会做许多智能任务,但它可进行 workflow 的相互交互(这可不简单)。活动对象由 “Activity”派生,提供出了强悍的功能。你可自如地创建你自己的活动,这个话题将 在第 13 章(自定义活动)进行介绍。事实上,本书的第二部分都是在介绍活动(第 7 章至第 13 章)。表 4-1 列出了许多我们通常感兴趣的 Activity 的属性,表 4-2 列出了 你会经常用到的方法。在第 13 章,你还会看到更多的和自定义活动相关的方法和属性。 表 4-1 活动(Activity)的属性属性 功能Description获取或设置用户定义的关于活动的描述。Enable获取或设置一个指明实例能否被执行和验证的值。获取实例最后运行的结果(ActivityExecutionResult)。(有 Canceled、Compensated、Faulted、None 和 Succeede ExecutionResult d)。得到 workflow 的状态,其为 ActivityExecutionStatus 的枚举值(Canceling、Closed、Compensating、Executing、 ExecutionStatus Faulting 和 Initialized)之一。Name获取或设置活动实例的名称。Parent获取包含本活动的父活动。WorkflowInstanceId获取和该活动相关的 workflow 实例的标识符。表 4-2 活动(Activity)的方法属性 功能Cancel取消活动的执行。Clone返回活动的一个深拷贝。Execute以同步方式运行活动。GetActivityByName假如在一个组合活动上执行,本方法将返回组合活动中所包含的指定名称的活动。Load从一个流中加载一个活动的实例。RaiseEvent触发一个和指定的依赖属性相关的事件。触发和所引用的依赖属性相关的事件。RaiseEvent 和 RaiseGenericEvent 的作用是一样的――第一个事件 RaiseEven RaiseGenericEvent&T& t 直接指出 DependencyPropenty,而 RaiseGenericEvent 则是一个泛型版本。 Save把活动保存到流中。活动的方法通常都具有虚拟和受保护的属性。目的是你可去覆盖它们,使其提供一 个符合你自己的活动所需要的实现。目前为止,最关键的方法是 Execute。当 workflow 运行时调用这个方法时,你的活动便开始执行了。 活动可被分为两个大类:组合活动和基本活动。组合活动包含其它活动。一个极好 的例子是我们目前为止贯穿书中的 Sequential 活动(译者注:它是基于顺序工作流中 所有活动的载体,在创建一个顺序工作流时 Visual Studio 就已为我们创建好了,可在 视图设计器中看到)。目前为止所有的程序实例执行 workflow 实例的方式都是 Sequen tial 活动,它包含其它活动,如它自身、Delay 活动和 Code 活动。 基本活动,就像我刚谈到的 Delay 活动和 Code 活动,它们是一个基于单一任务的 活动,我在本章早些时候谈过它。最终,你需要基本活动去实际承载特定的任务。组合 活动或许可指挥任务和数据的流动,但基本活动能做更多。 ActivityExecutionContext 对象 许多 Activity 对象的方法需要一个 ActivityExecutionContext 对象来进行输入。 在 workflow 运行时把你要执行的 workflow 实例入队的时候 ActivityExecutionContex t 对象被创建,因此,它不是你直接要创建的对象。workflow 运行时为你创建它。 ActivityExecutionContext 对象的作用是提供活动以方法和服务, 以便和 workflo w 实例挂钩。这些如初始化,定时器和产生执行流。它本质上是一个 helper 对象。在 1 3 章将更详细的对活动上下文(环境)进行讨论。 备注:假如你熟悉 ASP.NET 编程的话,这个 context 对象本质上和 System.Web.Ht tpContext 对象的作用是一样的。 其它相似的还有 System.Threading.Thread.CurrentC ontext。所有这些 Context 对象的目标都是一样的:提供一个存储位置并容易地恢复一 个当前执行实例的信息。这种情况下,它是一个执行当中的活动的一个实例。 依赖属性(Dependency Properties) 在表 4-2 中, 你将看到一些依赖属性 (DependencyProperty) 什么是依赖属性呢? 。 通常,假如你为类创建了一个属性的话,你也会在类中创建一个字段来存储该属性 的值。普遍的代码如下: class MyClass { protected Int32 _x=0;public Int32 X { get { return _x;} set { _x = } } }字段_x 更正式的叫法是 backing store。在这个例子中,你的类为 X 属性提供了 backi ng store。 然而,WF 和 WPF 通常都非常需要去访问你类中的属性。WPF 需要指明容器中控件的 空间和大小以便能最佳地被 render。WF 需要依赖属性来方便地进行活动绑定。WF 中 Ac tivityBind 类可为你方便地进行活动绑定。 活动验证 活动通常都具有验证能力,你可回忆第一章。 在第一章的例子中有这样一种情况,如在 IfElse 活动中未指定应该选择哪一个分 支进行执行的条件时,Visual Studio 会提醒我们。其它活动实现了不同的验证算法。 假如我们编译带有验证错误的代码,我们的编译都会失败。我们必须纠正这些验证条件 不充分的代码,才能编译和执行我们的 workflow 代码。 workflow 类型 你已创建过 workflow 应用程序,因此你可能注意到可以创建不同类型的 workflow 应用。 workflow 应用程序的类型很大程度上依赖于你选择的 root 活动。 尽管你注意到在新项目对话框中仅仅只有两种 workflow 类型的应用程序可供选择, 但实际运用中存在三种主要的类型。迄今为止本书中你已经创建过顺序工作流,因此它 们并不神秘。 当你创建 workflow 时,你的活动以你规定的顺序执行。 另一种从新项目对话框中看到的 workflow 类型是状态机工作流。我将在本章讨论 它的更多细节。 第三种 workflow 类型基于顺序工作流,但它是规则驱动的。它不是仅仅执行你指 定的任务, 而是由 Policy 活动和规则条件组成的基于规则的 workflow,来执行基于你指定的业务 规则 workflow 任务。 我们将在 12 章更多地学习这种 workflow 类型:“Policy 活动”。因为这种类型的 wor kflow 以顺序活动作为 root, 因此在新项目对话框中没有这种类型的 workflow 应用程序的模板可供选择。你应以顺 序工作流作为起始, 然后增加基于规则的活动。 选择一种 workflow 类型 在什么情况下一种类型的 workflow 比另一种类型的 workflow 更好?你如何选择合 适的 workflow 类型呢? 表 4-3 可为你提供一些基本的参考。 表 4-3 选择基本的 workflow 类型的判定表workflow 类型 适用条件workflow 任务可以自治的执行,很少由外部进行控制。主要由 workflow 自身来对执行的任务进行控制。只有少量用 顺序工作流 户或没有用户来和它进行交互。它的 root 活动是 SequentialWorkflow 活动。workflow 任务严重依赖外部来控制和指示其执行。预期有很多的用户交互(或其它外部控制)。对于基于状态的 wor 状态机工作流 kflow,root 活动是 StateMachineWorkflow 活动。业务逻辑中包含复杂的判断条件,既不像顺序工作流也不像状态机工作流。基于规则的工作流或者有一个顺序的 root 基于规则的工作流 活动,或者有一个基于状态的 root 活动。顺序工作流的理想应用是去执行业务处理。假如你需要从源中读数据,处理这些数 据,发送通知,往你的一个数据池中写入结果的话,顺序工作流或许将符合你的需求。 这并不意味着顺序工作流不适合处理依赖于用户交互的特定任务, 如赞同或不同意之类 的审批任务。其实这样一些的用户交互不应成为 workflow 自身的关注焦点。 假如你需要大量的用户交互,当你的 workflow 发送通知给用户或其它系统(有各 种原因:通知、需要批复、选择一个选项等等)以使用户或其它系统进行响应(它们的 响应来自事件)时,状态机工作流可能是更好的选择。这些事件触发了 workflow 从一 种处理状态转化到另一种处理状态。我将在本章后面及 14 章(“基于状态的工作流”) 更多地讨论这些。 最后一种 workflow 类型(我们将在 12 章看到)是基于规则的 workflow。这些 wor kflow 基于业务规则判定是否进行转化,并判定转化后的目标是什么。这些 workflow 通常都预置了更加复杂的剧情。 你或许会认为所有的 workflow 都能以基于规则的工作流类型来创建,但我们通常 并不总是使用这种方式进行创建。因为其它的 workflow 类型,如顺序工作流和状态机 工作流,它们能更容易地创建和测试。 要用最合适的 workflow 类型来构建你的系统。通常,在许多真实案例中你会发现 你自己使用了所有三种 workflow 类型的组合。 顺序活动 让我们进一步深入顺序复合活动吧。尽管迄今为止我们使用这些活动贯穿本书,但 我在之前有意地拖延谈论关于它的更多内容。现在我们去理解了 workflow 运行时和 wo rkflow 实例是怎样工作的, 并且知道 workflow 实例是我们正运行中的 workflow 活动的 版本,我们能更好的了解发生了什么。 执行顺序活动意味着这些活动以一个指定的顺序执行。首先要做的事最先执行,最 后才做的事最后执行。 一个顺序活动就像在根据目录执行。 你需要记下首先要做的任务, 接下来要做的任务和最后要做的任务。 假如这些任务以顺序活动的方式存储, 将以你 WF 指定的顺序精准地执行每个任务。 ` 备注:本书中我们不会看到以动态的方式添加活动,但你应知道这是可以做到的。 在 Visual Studio 中,workflow 的视图设计器可帮你展示你的 workflow。当你创 建一个顺序工作流应用程序并在设计器中打开 root 活动时,你可把任务放到屏幕的最 上方以便首先被执行。那些朝向底部的任务将晚些执行。从可视化界面可看出,活动运 行的顺序是从上到下。当然顺序活动还可以是一个复合活动。 创建顺序工作流 在本书中迄今为止我们已创建过一些顺序工作流应用程序, 因此这里我不再创建它 们。但我还是把完整的步骤重复一下。 建立一个顺序工作流应用程序 1.打开 Microsoft Vistual Studio 2008。 2.在文件菜单上,选择新建项目。然后将呈现新项目对话框。 3.在项目类型面板中,展开 Vistual C#树形节点,呈现出基于 workflow 项目的模 板。 4.在模板面板中,点击顺序工作流控制台应用程序或顺序工作流库。前者创建一个 可执行的应用程序并以控制台的方式执行, 而后者创建一个动态链接库并在其它应用程 序中使用。 5.输入你的项目或应用程序的名称。 6.输入或选择你想保存你的项目的所在路径。 7.点击确定,Visual Studio 2008 将为你建立一个基本项目,其中包含 workflow 视图设计器用户界面。 然后,你就可方便地从工具箱中拖拽你需要的活动,调整它们的属性以符合你的需 求。假如你需要增加更多 workflow 库的项目,你可参考我前一章中的描述,或者简单 地直接在你的应用程序中增加一个新的 workflow 类。 接下来我们还会看到大量的例子。 状态活动 迄今为止在本书中我们还未看到过状态机工作流。 章完全把焦点放到基于状态的 14 工作流的工作上,但我在这里将介绍一些概念,我们也会快速地创建一个基于状态的工 作流。 看看这样一个术语:有限状态机。我们把这个术语分成三个词:有限、状态和机器。 有限,意思是我们将进行转化的状态的数目是有限的。状态是我们的应用程序在事件发 生时进行转化的逻辑条件。机器则意味自动化。我将用一个例子来阐明。 在工程学校,或许会要求你使用有限状态机来设计一些数字系统。例如自动售货机 和洗衣机。看看自动售货机,思考一下机器工作必须具有的步骤,以便它能为你提供你 需要的商品(如汽水、糖果、点心等等)。当你投入硬币时,它会合计你投入的硬币金 额,直到你投入的硬币金额能购买商品时为止。当你选择一个商品时,它会检查存货清 单。假如有货,它就把你选择的东西分发给你。 我们可以使用有限状态机来构建自动售货机。在有限自动机的图示中,我们使用圆 来表示状态,箭头来表示状态之间的转换,转换由事件触发。图中有一个逻辑上的起点 和一个或多个逻辑上的终点。假如我们停在其它地方,我们的应用程序就被称作未指定 状态或者无效状态。我们的工作就是防止无效状态,如我们不能免费地获取商品,我们 也不应该接收超过商品价格的多余的钱。假如自动售货机接受了钱但又未提供商品的 话,用户毫无疑问会暴怒。 假象一下简化的自动售货机,让我们画出状态和导致状态转换的事件吧。正如我提 到的,状态用圆来表示。使你的机器从一个状态变为另一个状态的事件用箭头表示。它 们都可命名以便我们知道这些是什么状态和相关的转换。 我们毫无疑问需要一个开始状 态,如下图:图 4-2 有限状态机起始状态符号 这个状态表示机器所处的这样一个位置:等待有人来投入一个硬币。因此看看当有 人来投入一个硬币,但它还不够买一个商品的情况,我们通过创建一个新状态来进行模 拟,这个状态叫 WaitCoins(等待硬币)状态,通过 CoinInserted(投入硬币)事件转 换到该状态,如图 4-3:图 4-3 转换到 WaitCoins 状态 在用户投入足够金额的钱以能购买其中的商品之前, 机器一直处在 WaitCoins 状态, 并接受 CoinInserted 事件,否则会触发 SufficientCoins(金额足够)事件使我们的机 器转到 WaitSelection(等待选择)状态。在这里我们的自动售货机会耐心地等待用户 选择一个商品。 (在实际生活中,用户也能在任何时候要回投入的硬币,为了简单起见, 本例还是不考虑它吧。) 当用户选择商品后,商品会被分发给用户,我们的(状态)转换也就结束了。完成 状态,或者称作结束状态,由二个圆圈来指明,参见图 4-4。图 4-4 尽管这个自动售货机在现实世界中或许太过于简单, 但此处只是期望为你提供一个 的简要描述,使你明白状态机是如何工作的。当我们设计状态机时,我们指明其离散的 状态,或者逻辑位置来等待事件发生,然后我们指明转换机器状态的事件。有些事件可 让机器返回到同一状态,如开始状态。其它事件则会在一个新的事件被处理后使机器转 换到一个新的状态。没有事件被触发就没有状态的转换,理想情况下应没有无法预料的 事件或异常。 这种模型和我们用过的顺序工作流模型有很大的不同。在顺序工作流里,活动以指 定的顺序依次执行。一旦在该顺序链上的一个活动执行完它的任务,在该链上的下一个 活动就开始执行它的工作。在 workflow 处理中或许有事件参与其中,但它们在 workfl ow 任务的处理(如定时器事件)上相对简单。 但状态机会花费大量时间在等待上。它们等待事件,依赖事件来使它们的状态进行 转换。状态自身不会激发事件(尽管它们可能会调用外部代码)。它们就是事件处理, 因此它们会耐心地等待它们需要的事件来进行状态的转换。依靠事件,它完全可能从一 个状态切换到任何一个离散的不同的状态。 假如我们的自动售货机处在 WaitCoins 状态, 当接受一个 CoinInserted 事件、 RefundRequested 事件或 ImminentPowerdown 事件时会 分别做不同的事情。但我并未在图 4-4 中这个经过简化的模型里画出这些事件,但我相 信你能看懂不同的事件是怎样驱动你的有限状态机转换到不同状态的。 在 WF 里,基于状态的 workflow 内的个别状态由 State 活动创建。State 活动是一 个复合(组合)活动,但它对容纳的子活动有限制。你将在 14 章学习到基于状态的 wo rkflow 的更多东西。 备注:正如顺序工作流使用一个特别的 Sequence 活动来容纳整个工作流一样,基 于状态的工作流也有一个特别的 root 活动做这事, 这就是 StateMachineWorkflow 活动, 它是一个特别的 State 活动。特别之处是它是必须的,这样当初始化执行时 root 活动 就能接受初始化参数。 创建一个状态机工作流应用程序 怎样创建一个基于状态的 workflow 呢?创建一个基于状态的工作流和创建一个顺 序工作流一样容易。我们现在就来看看怎样创建一个状态机工作流。然而我们现在不会 添加任何代码――虽然本书后面有很多时候需我们这样去做, 但我们现在只要需要了解 怎样创建一个状态机工作流就行了。 创建一个状态机工作流应用 1.启动 Microsoft Visual Studio 2008。 2.在文件菜单上,选择新建一个项目,这将打开新建项目对话框。 3.展开项目类型面板中的 Visual C#节点,这将显示所有使用 C#语言的项目类型。 4.在 Visual C#节点下点击 Workflow 节点,这将显示所有基于工作流的项目模板。 5.在模板面板内,点击状态机工作流控制台应用程序或状态机工作流库。如下图所 示: 6.输入程序或项目的名称。 7.输入或选择项目文件要保存的位置。 8.点击确定。Visual Studio 2008 将为你创建一个包含 workflow 视图设计器用户 界面的项目,如下图: 我们需要去触发引发工作流改变状态的事件,因此,我们需理解 workflow 实例是 怎样和它们的宿主应用程序进行通信的。我们将在第八章(“调用外部方法”)看到宿 主和 workflow 之间的通信,在第十章(“事件活动”)中我们将学习状态机工作流的 事件驱动。 【翻译】WF 从入门到精通(第五章):workflow 跟踪上一篇:【翻译】WF 从入门到精通(第四章):活动及 workflow 类型介绍学习完本章,你将掌握: 1.workflow 的可选服务 2.创建一个事件跟踪数据库 3.激活事件跟踪服务 4.创建一个自定义跟踪 5.查看你的 workflow 的跟踪信息目前为止, 我们看过 workflow 的一些基本对象。 我们通过活动创建 workflow 任务, 它们在执行时由 WorkflowInstance 对象进行管理。workflow 实例由 WorkflowRuntime 编入队列并进行控制。但 WF 不只是为我们提供了这些对象,它也为我们提供了一些服 务来和这些对象一起协同工作。可插拔(可选)服务工作流服务是一些附加的软件库,你的工作流能使用它来完成它们的的任务。有些 服务是非必须可选的,如本章介绍的跟踪服务。而其它的服务需要你的工作流必须执行 它。 一个可插拔服务是这样一个服务,它能像照菜单点菜一样被选中以执行特定任务。 例如,有管理线程的服务、跟踪的服务、事务服务等等。你可选择那些适合你的工作流 的服务。你甚至还能自己进行创建。 哪这些服务看起来像什么?他们能为我们做什么?表 5-1 列出了可获取的基本服 务,它很好地为你描述了这些可获取的服务的概念,并告诉你他们能做什么。 当中的大部分服务我们不会直接使用。 我们普遍的用法是使用从这些基本服务派生 出的服务。 表 5-1 基本工作流服务 服务功能WorkflowPersistenceService抽象基类,派生出所有持久化的服务。该基类为你提供了一些方法, 使你能用来管理和一个工作流实例相关的工 WorkflowQueuingService 作流队列。WorkflowRuntimeService抽象基类,派生出工作流运行时的内核服务。WorkflowScheddulerService所有在工作流运行时宿主上创建线程以运行工作流实例的类的基类。WorkflowSubscriptionService那些管理订阅(Subscriptions)工作流运行时类的基类。WorkflowTransactionService所有事务服务的基类。一个抽象基类,在跟踪服务和运行时跟踪基础结构(infrastructure)间 TrackingService 提供了基本的接口。请记住这些是基类。我们使用的服务实际上从它们派生。例如,当我们运行一个工 作流实例时,有时需为实例创建一个线程去使用。DefaultWorkflowSchedulerService 正是做这个工作的,它使用 WorkflowSchedulerService 作为它的基类。但假如你想自 己提供这个线程,你可使用 ManualWorkflowSchedulerService 代替。在本章中我们将 看到由 SqlTrackingService 提供的跟踪服务,它使用了 TrackingService 作为它的基 类。 “可插拔(可选)”一词部分来源于下面的情况:你可能考虑在任何时间上你都可 能需要使用一个调度程序服务,运行时服务,入队和订阅(定时器)服务。但你还能在 工作中进一步添加持久化和跟踪服务,以及外部数据通信服务。工作流跟踪在本章,我们将把重点放到跟踪服务上。其它服务将在其它章节进行介绍。WF 由一 个主要的跟踪服务――SqlTrackingService 承载。 但是假如你需要的话, 也有两个额外 的服务可用。 它们是 ConsoleTrackingService 和 SimpleFileTrackingService, 这二个 服务允许你把跟踪信息写到控制台窗口或者文件中而不是 Microsoft SQL Server 数据 库。在这里我们不会使用这两种服务,但你需要的话你可使用它们。使用 SqlTrackingService 进行工作流事件跟踪通过添加一个跟踪服务(通常是 SqlTrackingService)到工作流运行时中,你可跟 踪你的工作流的处理过程。 假如你有特定的跟踪需求, 你也能创建你自定义的跟踪事件。 假如捕获的事件为你提供了过多的跟踪数据, 你也能创建跟踪配置文件来过滤这些跟踪 数据。 当跟踪的事件激发时,WF 创建并管理跟踪记录。尽管你不用做这些工作,但你还是 能容易地从 WF 中直接访问这些跟踪记录。你要知道这些信息也被记录到数据库中,因 此直接从数据库中检索这些信息也是可能的。通常都在记录这些跟踪信息后的某个时 间,使用一个象 WorkflowMonitor 或你自己设计的工具之类的外部跟踪监控工具,来查 询这些跟踪信息。 表 5-2 列出了在你的 WF 事件跟踪中经常使用的对象,在本章我们将使用其中的一 些。假如你需要自定义你的工作流事件跟踪,那你应知道 WF 为你提供了一个和跟踪相 关对象的强大类库。 表 5-2 事件跟踪对象对象 功能指定要从活动中提取并在跟踪点匹配时与关联的批注集合一起发送到跟踪服务的属性或字段。表示一个条件, 该条件通过使用指定的比较运算符将活动成员的值与指定 ActivityTrackingCondition 值进行比较。定义与根工作流实例的可能执行路径中的某个活动状态事件相对应的活 ActivityTrackingLocation 动限定位置。包含运行库跟踪基础结构在 ActivityTrackPoint 匹配时发送到跟踪服 ActivityTrackingRecord 务的数据。它还用在 ActivityEvents 属性的返回列表中。定义工作流实例的可能执行路径中要跟踪的点, 该点与活动执行状态更改 ActivityTrackPoint 关联。包含用于管理跟踪数据查询的方法和属性, 跟踪数据包含在 SqlTracking SqlTrackingQuery Service 使用的 SQL 数据库中。包含一些属性,这些属性用于约束 SqlTrackingQuery.GetWorkflows 调 SqlTrackingQueryOptions 用所返回 SqlTrackingWorkflowInstance 对象的集合。通过工作流实例的 SqlTrackingService 提供对 SQL 数据库中保留的跟踪 SqlTrackingWorkflowInstance 数据的访问 定义根工作流实例的可能执行路径中的关注点, 应将有关该关注点的信息通知跟踪服务。它过滤跟踪事件,并把过滤后的跟踪记录返回给某个跟踪 TrackingProfile 服务。这里有三种类型的跟踪事件能被过滤:活动状态事件、工作流状态事件和用户事件。定义与根工作流实例的可能执行路径中的某个用户事件相对应的活动限 UserTrackingLocation 定位置。包含运行库跟踪基础结构在 UserTrackPoint 匹配时发送到跟踪服务的 UserTrackingRecord 数据。定义一个要跟踪的点(与用户事件关联),该点位于根工作流实例的可能 UserTrackPoint 执行路径中。指定要从工作流的根活动中提取, 并在跟踪点匹配时随关联的批注集合一 WorkflowDataTrackingExtract 起发送到跟踪服务的属性或字段。定义对发生在根工作流实例中的特定工作流事件的关注; 用于按跟踪配置 WorkflowTrackingLocation 文件中的 WorkflowTrackPoint 进行匹配。包含运行时跟踪基础结构在匹配了 WorkflowTrackPoint 时发送到跟踪 WorkflowTrackingRecord 服务的数据。它还用在 WorkflowEvents 属性的返回列表中。定义一个与一组工作流状态事件关联的点, 这些事件在根工作流实例的可 WorkflowTrackPoint 能执行路径中进行跟踪。这些对象可考虑归为两个大类:跟踪数据检索和跟踪详细说明。跟踪检索对象,如 SqlTrackingQuery,一旦跟踪数据被存储到数据库中,你可使用它们采集跟踪数据。跟 踪详细说明对象,如跟踪点和位置对象,允许你能在工作流代码中控制该跟踪什么。 像跟踪点和位置对象之类的跟踪详细说明对象还可被归为三大组:活动事件、工作 流事件和用户事件。和活动相关的跟踪对象,如 ActivityTrackingPoint 或 Activity TrackingLocation,用来记录相关联的活动的事件信息并保存到跟踪数据库中。这些事 件包含如下这些:活动取消、未处理的异常和执行的事件。工作流事件跟踪对象的工作 方式和工作流相关的事件的工作方式相像(但工作流启动和停止,实例的创建、空闲和 完成及其它相似的相关联的事件除外)。最后是用户事件跟踪,它用在自定义你特有的 工作流跟踪需求中指定你的工作流并完全依赖于你的工作流想怎样进行跟踪。 在本章中 当我们学习跟踪配置文件时会看到它们中的几个。 跟踪记录通过批注加以装饰。 批注是一些保存跟踪记录并被记录进跟踪数据库的字 符串。关联活动和关联工作流的跟踪记录有一个创建好的批注的集合,但你可以为用户 关联事件的跟踪记录提供一个额外的批注。 在 WF 中跟踪这一术语和平常“跟踪”的概念没有什么不同。平常意义上的“跟踪” 是一个有用的调试工具,在 ASP.NET、像 WPF 之类的.NET 技术及 Windows Forms 中都支 持跟踪调试的能力。跟踪允许你过滤跟踪信息记录以满足你的需要,你既可只看异常的 跟踪信息,也能看到整个跟踪栈。 WF 跟踪基于相似的概念,事实上是过滤。正如你可能想到的,关联活动事件和关联 工作流事件将产生所有类型的跟踪记录,你或许能从中找到感兴趣的记录(如未处理的 异常或空闲状态),你可以决定其它的事件不用进行跟踪。 为过滤掉你不想进行跟踪的事件,你要创建一个跟踪配置文件。一个跟踪配置文件 是一个 XML 文档,它指明了跟踪的对象和要排除跟踪的对象。和跟踪不同,跟踪配置文 件指明哪些东西要写入跟踪数据库,而不是指明以后哪些东西能被查看到。假如你排除 了一些事件,这些排除的事件就不会向数据库里写任何东西。和跟踪的另一个不同之处 是,跟踪配置文件的 XML 文档也被记录进跟踪数据库,当执行工作流时被恢复。换句话 说,跟踪记录了指定的要去跟踪的任何东西,但不进行跟踪信息归类。设置 SQL Server 进行跟踪尽管你可创建自定义的跟踪服务来把跟踪数据记录进各种存储中 (如消息队列或数 据文件),但本章,我们将把注意力放到 SQL Server 2005 数据库上,WF 有能力把事件 数据记录到 SQL Server 2005 数据库中。 为使用 SQL Server 2005 提供了内置的创建 WF 支持。 我们先在 SQL Server Management Studio (或者 Express 版本) 中创建一个新的数 据库。然后需运行一些由 WinFX 组件提供的 SQL 脚本,这些脚本将创建数据库角色、表 和视图、必须的存储过程以和你的工作流进行交互。我们就来通过创建一个新数据库并 运行一些准备好的脚本来开始吧,然后我们将使用 WF 跟踪服务记录下跟踪数据并写入 数据库。 备注: 我在下面的步骤中使用 SQL Server Express,但这些步骤对于其它版本的 SQ L Server 2005 同样适用。 创建一个 SQL Server 2005 跟踪数据库 1.启动 SQL Server Management Studio,连接数据库引擎。2.在数据库节点上单击右键激活右键快捷菜单,选择“新数据库”。 3.在新数据库对话框中输入“WorkflowTracking”作为数据库的名称字段,点击确 定。 4.下一步将执行 WF 为设置跟踪所提供的脚本(这会创建表、视图以及工作流跟踪 的角色)。这些脚本的位置在&%WINDIR%&\Microsoft.NET\Framework\3.0\Windows Wor kflow Foundation\SQL\ZH-CHS,在这里&%WINDIR%&是指你的 Windows 目录(通常是 C:\ Widows)。在 SQL Server Management Studio 打开 Tracking_Schema.sql 文件。 5.SQL Server Management Studio 会在一个新窗口中导入文件中的脚本, 但在我们 运行脚本前,我们需指明在哪个数据库中运行这些脚本,因此我们要选择 WorkflowTra cking 数据库。 6.点击工具栏上的执行按钮执行这些脚本。 7.重复 4-6 步执行 Tracking_Logic.sql 脚本。这将在数据库中创建必须的存储过 程。 我们现在就创建了一个将记录跟踪信息的数据库, 但怎样得到已记录的信息呢?什么组 件进行这方面的工作呢?让我们看看!使用 SqlTrackingServer 服务在工作流跟踪数据库设置好后,现在就是实际使用它的时候了。我们先创建一个新 的工作流并看看我们怎样去跟踪事件。我们将创建一个稍微复杂一些的工作流,里面有 几个事件可以提供给我们去进行跟踪。在我们创建一个原始的工作流后,我们将增加必 要的跟踪代码。 创建一个新工作流并进行跟踪 1.为更方便些, 我已创建了两个版本的样例应用程序。 Workflow 包含两个不同版本 的应用程序:一个是不完全版本,一个是完全版本。完全版本已完全编写完成并可直接 运行,非完全版本可方便你进行修改,并按步骤完成相应练习。你可通过本章后面的下 载链接下载这些项目文件。 2.下载本章源代码,打开 TrackedWorkflow 解决方案,像第三章中相应步骤一样创 建一个顺序工作流库的项目,名称为 TrackedWorkflow。 3.在你完成以上步骤后,Visual Studio 会打开工作流设计器以便进行编辑。 4.从工具箱中拖动一个 IfElse 活动到设计器界面上。如下图:5.单击左边的 ifElseBranchActivity1 分支,激活它的属性使其在 Visual Studio 中的属性窗口中显示。 6.寻找 ifElseBranchActivity1 的 Condition 属性。 点击下拉列表框上向下的箭 头打开下拉列表框,选择其中的代码条件节点。如下图:7.Condition 属性现在会在它的左边呈现出一个“+”号。单击这个+号展开其属性 网格,这会暴露出 Condition 属性的 Condition 名称字段。在编辑框中,输入 QueryDe lay。我们将使用这个方法来决定我们将执行 IfElse 活动的那个分支。 8.下一步我们在左边的分支(这个分支在条件值为 True 时执行)添加一些活动。 首先,从工具箱中拖拽一个 Code 活动到 IfElse 的左边分支即 ifElseBranchActivity1 上。 9.你看到的惊叹号标记的意思在前面的章节我已描述过,意思是我们还有工作要 做。在这里,它指出我们需添加一个方法,Code 活动添加到工作流中执行时将调用这个 方法。在 Visual Studio 的属性面板上,定位到 ExecuteCode 属性,在该编辑框中输 入 PreDelayMessage。10.也许你要看看我要做什么……其实就是添加一个延时工作流,在第三章我们已 经创建过。就像在第三章做的一样,再拖拽一个 Delay 活动和另一个 Code 活动进 ifEl seBranchActivity1 中,然后设置它们的属性。Delay 活动延时 10 秒(00:00:10),第 二个 Code 活动执行一个名称为 PostDelayMessage 的方法。 完成这些步骤后的设计器界 面如下图所示: 11.在设计器中的工作完成后,我们就来添加相应代码。在解决方案资源管理器中 的 Workflow1.cs 文件上单击右键,选择查看代码。然后在项目中添加对 System.Windo ws.Forms 的引用,然后在 Workflow1.cs 文件的顶部声明和其对应的下面的名称空间。 using System.Windows.F 12.你查看这个文件,你会看到 Visual Studio 为你添加的作为活动属性的三个事 件处理程序:PreDelayMessage、PostDelayMessage 和 QueryDelay。和第三章类似,在 Code 活动中添加消息对话框,以使应用程序能在工作流执行时通知你。对于 PreDelayM essage,添加下面的代码: MessageBox.Show(&Pre-delay code is being executed.&); 对于 PostDelayMessage,添加下面的代码: MessageBox.Show(&Post-delay code is being executed.&); 13.我们些许更感兴趣的是在 QueryDelay 中添加的以下代码: e.Result = // 假定我们不延时if (MessageBox.Show(&Okay to execute delay in workflow processing?&, &Query Delay&, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { // 需进行延时处理 e.Result = // 显示消息Console.WriteLine(&Delay path taken&);} // if else { // 显示消息Console.WriteLine(&Delay path NOT taken&);} // else14.完成上述步骤后,我们需要为我们的 WorkflowTracker 主应用程序添加对工作 流项目 TrackedWorkflow 的项目引用,步骤略。 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 1.WF 由活动和工作流事件跟踪能力承载, 因此我们不需为跟踪事件做太多工作。 尽 管如此,我们仍然需在主程序文件中添加一些逻辑。首先,要为 WorkflowTracker 应用 程序添加 System.Configuration 引用,我们需要它来把访问数据库的连接字符串存储 到应用程序的配置文件里。 2.下一步,为 WorkflowTracker 应用程序添加一个应用程序配置文件。方法是在 V isual Studio 的解决方案管理器中的 WorkflowTracker 树节点上单击右键, 依次选择添 加、新建项。在呈现的添加新项对话框中选择应用程序配置文件,点击确定。这就为我 们的应用程序添加了一个新的 app.config 文件。参见下图: 3.打开 app.config 文件,在 Configuration 的开始标记和结束标记间插入下面的 内容:&connectionStrings& &add name=&TrackingDatabase& connectionString=&Data Source=(local)\SQLEXPRESS;Initial Catalog=Workflow TIntegrated Security=T&/&&/connectionStrings&备注:上面的连接字符串可能和你实际应用中有所不同,你需要灵活进行配置。 4.点击 WorkflowTracker 项目中的 WorkflowFactory.cs 文件,查看其代码。 5.在该文件中声明以下名称空间(需添加 System.Configuration 引用): using System.Workflow.Runtime.T using System.C 6.在 WorkflowFactory.cs 文件中,找到我们创建 WorkflowRuntime 实例的地方, 在这里我们需要为 WorkflowRuntime 引入 SqlTrackingService。 GetWorkflowRuntim 在 e 方法中添加下面的代码:String conn = ConfigurationManager.ConnectionStrings[&TrackingDatabase&].ConnectionS _workflowRuntime.AddService(new SqlTrackingService(conn));完成了上述步骤,我们就添加了实际中要去执行跟踪的代码(稍后,我们会添加更 多的代码来显示跟踪结果)。编译该解决方案,然后按 F5 或 Ctrl+F5 执行它。 备注:假如程序中出现 ArgumentException 异常,最可能的原因是运行时没有访 问数据库的权限。 假如工作流运行正常, 你可在 WorkflowTracking 数据库的 ActivityInstance 表中 看到下图 5-1 中显示的结果。图 5-1 表 ActivityInstance 中的记录检索来自于工作流的跟踪记录 1.打开 WorkflowTracker 项目中的 Program.cs 文件。 2.在文件中声明以下名称空间: using System.C using System.Workflow.Runtime.T 3.在 Main 方法中,找到下面的代码: waitHandle.WaitOne(); 4.在上面的代码下添加以下的的代码: ShowWorkflowTrackingEvents(instance.InstanceId); ShowActivityTrackingEvents(instance.InstanceId); 5.上面我们调用的一组方法并不存在,我们需要添加它们。在 Program 类中添加 这些方法:Code在最后一步中忽然冒出大量的代码,但实际上并不太复杂。我们首先创建了一个 S qlTrackingQuery 的实例, 为它提供了我们曾提供给 SqlTrackingService 的相同的连接 字符串。然后我们通过当前工作流实例的 ID(一个 Guid)标识,从数据库中查询该实例 的跟踪信息。该查询由 SqlTrackingService.TryGetWorkflow 执行。假如数据库中有我 们指定的工作流的跟踪信息,我们循环获取跟踪记录(查询返回给我们的是一个 workf lowTrackingRecord 对象的集合),从中提取我们感兴趣的信息。假如查询结果中没有 记录,也就没有跟踪信息写到控制台窗口中。最终的屏幕的输出结果如图 5-2 所示(在 调试模式下运行代码的话,你或许需要设置一个断点才能看到下图的输出结果)。 跟踪用户事件SqlTrackingService 是 WF 的一部分,它具有跟踪事件的能力。也就是说,它能跟 踪活动和工作流激发的标准事件。但由你生成的事件呢?我们又如何跟踪它们呢? Activity 活动支持一个名叫 TrackData 的方法,TrackData 有两个重载版本:一个 版本接受一个要存储进跟踪数据库中的对象, 另一个版本接受一个字符串类型的键及一 个要存储进跟踪数据库中的对象。 假如你执行 TrackData 并为跟踪传入通常是字符串类型的数据, 那这些信息将作为 用户事件数据存入跟踪数据库。 检索来自你的工作流的跟踪记录 1.打开 WorkflowTracker 项目中的 Workflow1.cs 文件。 2.找到我们在创建工作流时添加的 PreDelayMessage 方法和 PostDelayMessage 方 法。 3.在名为 PreDelayMessage 的方法内的显示信息对话框的代码下面添加以下代码: this.TrackData(&Delay commencing&); 4.同样,在名为 PostDelayMessage 的方法内的显示信息对话框的代码下面添加以 下代码: this.TrackData(&Delay completed&); 5.编译并执行。 现在打开 WorkflowTracking 数据库中的 UserEvent 表,里面有两行,我们在工作 流中每调用 TrackData 一次就产生一条记录,表中部分内容如图 5-3 所示。图 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”指在你的工作流中活动、工作流或用户相关的事件发生时的一 个}

我要回帖

更多关于 景区自动售票机 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信