首页 > 编程开发 > Objective-C编程 >
-
WF从入门到精通之基本活动的操作
学习完本章,你将掌握:
1.知道怎样使用Sequence活动
2.知道怎样使用Code活动
3.知道在工作流中怎样抛出异常并对其进行处理
4.知道如何在代码中暂停和终止你的工作流实例
在本章,我们将正式引入前面已经看到过的一组活动:Sequence活动和Code活动。但我相信,适当的错误处理对于精心设计和运行良好的软件是至关重要的,所以我们将会研究如何使用工作流中的活动抛出异常、捕获异常、甚至暂停和终止你的工作流。我们就从Sequence活动开始吧。
使用顺序活动对象
实际上,说我们已见过Sequence活动并不完全正确。我们创建工作流应用程序时实际上使用的是SequentialWorkflow活动,但大体的意思是一样的:这个活动包含其它依次要执行的活动。这一点可和使用parallel活动的并行执行相对比,在第11章(“Parallel活动”)中我们将看到parallel活动。
当你以特定的顺序执行任务时,你必须依次完成这些任务,这点通常是必须的。
Sequence活动是一个组合活动,我们在第四章(“活动和工作流类型介绍”)中已经简要讨论过。它包含其它活动,这些活动一定要按次序执行。你可在父Sequence活动内放入包含parallel活动在内的其它组合活动。但子活动要依次地,一个接一个地执行,即使这些子活动本身包含的并行执行流也如此。
我们就来使用Sequence活动创建一个简单的工作流。我们将再次使用Code活动,关于它的更详细的细节将在下一节“使用Code活动”进行讨论。为对特定的工作流活动的行为进行了解,我们将回到基于控制台的应用程序中。对于基于控制台的应用程序,通常你需要书写的代码更少,因为你不用对用户界面进行处理。(但随着本书的进展,我们也会创建其它的图形化的测试案例。)
创建一个使用了Sequence活动的工作流
1.下载本章的源代码,本例的最终版本在“Sequencer Completed”目录下,可使用Visual Studio 2008打开并直接查看它的运行结果。“Sequencer”目录下则为练习版本,我们将从该版本开始本例的学习,首先使用Visual Studio 2008打开该解决方案。
2.在我们的解决方案中添加一个顺序工作流库的项目,项目名称为“SequencerFlow”。
3.从工具箱中拖拽一个Sequence活动到Visual Studio的工作流视图设计器上。
4.然后,从工具箱中拖拽一个Code活动到我们刚添加的Sequence活动中。
5.在该活动的ExecuteCode属性中输入“DoTaskOne”,然后按下回车键。
6.Visual Studio会自动把我们带到代码编辑状态。定位到Visual Studio刚刚添加的DoTaskOne方法,然后再该方法内输入下面的代码:
Console.WriteLine("Executing Task One...");
7.反复执行步骤4、5、6两次,添加方法“DoTaskTwo”和“DoTaskThree”,然后在这些方法中修改Console.WriteLine输出的内容(“One”依次改为“Two”、“Three”)。该工作流的视图设计器现如下图所示:
8.回到主应用程序,打开Program.cs文件,定位到Main方法上。在该方法中,找到下面的代码:
Console.WriteLine("Waiting for workflow completion.");
9.在你找到的这行代码下添加下面的代码:
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(SequencerFlow.Workflow1));
instance.Start();
10.当然,我们需要在主应用程序项目中引用该SequencerFlow工作流库。
11.编译该应用程序,纠正任何出现的错误。按下F5或Ctrl+F5运行该应用程序。设置一个断点或从命令提示符下运行该程序,这样你就能看到输出结果,结果如下:
正如你从步骤11看到的,该任务和我们所期望的顺序依次被执行。需记住两方面:Sequence活动是一个组合活动(其它活动的容器),其次就是它容纳的活动以顺序依次执行的。
使用Code活动
迄今为止,在本书中我们经常使用的另一个活动是Code活动。Code活动就是要让你的工作流执行你所提供的自定义代码。在下一章我们将看到,还有一种方法可调用外部方法。
当你把一个Code活动放入你的工作流中时,它的ExecuteCode属性会被设置为工作流运行时将调用的方法的名称。
实际上,当你在刚刚完成的Sequencer应用程序设置ExecuteCode属性时,假如你仔细看看Visual Studio为你插入的代码,它虽不是一个被调用的方法,但也差不多,它其实是一个事件处理,下面是我们插入代码后的DoTaskOne方法:
private void DoTaskOne(object sender, EventArgs e)
{
Console.WriteLine("Executing Task One");
}
正如你看到的,当工作流运行时在执行你的Code活动时,它会触发一个事件,该事件的名称就是你在ExecuteCode属性中设置的值。我们将在本书的剩余部分中好好利用这个Code活动。
使用Throw活动
在本书中很早以前就提到过该活动,但我还没有真正加深这个基本的工作流处理模型的概念。因此,我们需能对真实世界中的各种各样的情况进行建模,这其中就包含我们需要抛出一个异常的情况。假设有些事情在前进的道路上并不平坦,我们的软件并不能为防止抛出异常而处理任何其它任何情况。假如我们乐意的话,我们可使用C#中的throw关键字来抛出一个异常,但在工作流中,我们使用一个特别的活动来做这些事,并使用一个特别的活动来处理这些异常,这些我们将在下节看到。假如我们使用C#中的throw关键字的话,工作流运行时会“swallows(淹没)”该异常,并不会给出通知信息。
这种现象的原因是Throw活动。当工作流运行时遭遇Throw活动时,假如没有相关的失败处理操作,工作流运行时将触发WorkflowTerminated事件。但请记住,届时,工作流实例会被终止,工作流运行时会被停止。在这时任何更正异常状况的任何尝试都已为时已晚,我们仅能做的是重启工作流并开始一个新的工作流实例。假如我们想在终止前更早地处理异常,我们需要使用Throw和FaultHandler活动组合。
备注:推荐的练习使用了Throw和FaultHandler组合而不是单一的Throw。使用Throw活动本身等同于在传统应用程序代码中使用没有进行异常处理的C#的throw关键字。在本节,我们将单独使用Throw来看看会发生什么。在下一节,我们将使用Throw和FaultHandler活动组合来看看他们怎样协同工作。
回到我们关注的Throw活动,当你拖拽一个Throw活动到设计器上后,你可找到两个需进行设置的属性。首先是FaultType属性,该属性告知Throw活动将抛出什么类型的异常;另一个是Fault属性,假如此时Throw活动抛出的异常不为空,它就指示该Throw活动引发的异常对象。
FaultType属性不必做大量的解释,它简单地告知工作流实例将抛出的异常类型。我们没有指明的异常由工作流运行时进行处理或者被忽略(假如你想处理的话,也可在以后进行处理)。
但Fault属性的背后有什么密码呢?假如设置了该属性的话,它才真正会是Throw活动所使用异常。假如该属性为空的话,Throw活动仍旧抛出一个FaultType指定的类型的异常,但它是一个新的异常,该异常没有既定的消息(记住,Message属性为我们提供了一些除它本身的异常类型之外的关于错误的一些描述)。
假如你想让Throw活动抛出一个带有详细Message的异常,你需要使用new操作符创建该异常的一个实例并把它指定到你绑定的Throw活动的相同属性上。
我再以略微不同的语言来表达上述这些。Throw活动,更具体地说,它的Fault属性会和你的工作流中所选择的活动(包括root活动)中的具有同一种异常类型的一个属性绑定到一起。就是说,假如你有一个抛出类型异常为NullReferenceException的Throw活动,你就必须在你的工作流中的一些活动上提供一个类型为NullReferenceException的属性以让Throw活动使用。然后让Throw活动绑定这些活动的属性,以便它能使用你用new操作符产生的同一个异常。
在这里,我们会写一些代码来进行试验。我们就开始创建一个使用了Throw活动的小工作流来看看它是怎样工作的。
创建一个使用了Throw活动的工作流
1.在下载的本章的源代码内有两个名称为“ErrorThrower Completed”和“ErrorThrower”的文件夹,“ErrorThrower Completed”文件夹内为本例的完整代码,“ErrorThrower”文件夹内为本例的练习项目。我们现在就使用Visual Studio打开“ErrorThrower”文件夹内的项目。
2.打开ErrorThrower后,向该解决方案添加一个顺序工作流库的项目,名称为ErrorFlow。
3.从工具箱中拖拽一个Code活动到Visual Studio工作流视图设计器上,设置该Code活动的ExecuteCode属性的值为“PreThrow”。
4.然后,从工具箱中拖拽一个Throw活动到设计器上,位置在上一步添加的Code活动的下面。
5.在Throw活动的属性面板上,选中它的FaultType属性,然后点击浏览(...)按钮。(以三个点作为text的按钮通常表示浏览。)
6.这将出现一个“浏览并选择.NET类型”对话框。在里面,选择Throw活动将构建的异常类型。我们输入或选择“System.Exception”,然后点击确定。
7.选中Throw活动的Fault属性,然后点击它浏览(...)按钮。
备注:未设置Fault属性,甚至未设置FaultType属性都不会导致编译失败。但是,这将设置一个message内容为“Fault属性未设置”的System.Exception类型的异常。
8.这将弹出“将Fault绑定到活动的属性”对话框。因为我们还未添加fault代码,因此我们点击“绑定到新成员”选项卡,然后在“新成员名称”中输入WorkflowException,最后点击确定。这就为你的root活动添加了WorkflowException属性。
9.添加第二个Code活动,设置它的ExecuteCode属性的值为“PostThrow”。此时的视图设计器界面如下:
10.现在我们的工作流就创建好了,然后将添加相应的代码。查看Workflow1.cs的代码,找到前面步骤添加的PreThrow事件处理程序,添加下面的代码:
Console.WriteLine("Pre-throwing the exception");
WorkflowException = new Exception(
"This exception thrown for test and evaluation purposes");
11.同样,找到PostThrow事件处理程序并添加下面的代码:
Console.WriteLine("Post-throwing the exception (You won't see this output!)");
12.工作流现在就设计完成了,我们现在回到主应用程序继续工作。打开Program.cs文件,定位到Main方法。在Main方法内,找到下面的代码:
// Print banner.
Console.WriteLine("Waiting for workflow completion.");
13.在上面的代码下,添加下面的代码:
WorkflowInstance instance = workflowRuntime.CreateWorkflow(typeof(ErrorFlow.Workflow1));
instance.Start();
14.现在,为主应用程序添加对ErrorFlow工作流库的项目引用。
15.编译应用程序,纠正任何编译错误。按下F5或Ctrl+F5运行该应用程序。你将看到下面的结果:
假如你仔细看看输出结果,你将看到WorkflowTermination事件处理会被调用,并为我们显示了终止的原因:有一个异常。该异常的Message和我们在步骤10中添加的类型为Exception的WorkflowException异常的相关信息相匹配。
备注:当你像在步骤8中一样添加新的属性时,这些由Visual Studio插入的属性就是依赖属性(看看第四章)。
现在我们看了在WF中异常是怎样构建的,我们又怎么捕获处理它们呢?毕竟,在工作流的终止事件处理程序中处理异常的话通常都太迟,对于我们没有任何价值。还好,WF为我们提供了FaultHandler活动,我们现在就来看看。
使用FaultHandler活动
使用FaultHandler的方式和我们目前为止看到过的其它任何活动的使用方式有细小的差别。其实,我们更应该仔细地去看看视图设计器。为什么呢?因为相比其它的工作流活动,错误处理有一个单独的设计界面(其实,还有第三个设计界面,它为取消处理服务,我们也将在此看到)。
备注:在第15章(“工作流和事务”)中,我们将看到补偿活动,它包含补偿事务。处理错误就是其中的一部分。补偿的意思是,产生一个动作,以减轻异常可能带来的危害。
快速浏览工作流视图设计器
在此时,假如你创建过我给出的工作流实例的话,你可能会对拖拽活动到工作流视图设计器上,然后设置它们的属性,然后编译并执行基本工作流的代码的这一系列的工作方式感到满意。然而,我还有一些东西没有告诉你,我保留它们的原因是因为我们此前的焦点是放在工作流程序的编写和执行上。
但现在,你已经使用过工作流中的设计工具,体会过工作流的编写,Visual Studio的使用。我们就花点时间来看看Visual Studio提供的在工作流辅助设计方面的其它东西,这主要有两个,简要的说就是:附加的视图设计器界面和调试。
附加的视图设计器界面
假如你回头看看一至六章,你会看到那些我们从工具箱拖拽活动到Visual Studio为我们呈现的视图设计器界面上的例子。但你注意到视图设计器窗口右键快捷菜单中的“查看工作流”、“查看取消处理程序”、“查看错误处理程序”三个菜单项没有?如下图所示:
图7-1 视图设计器界面右键快捷菜单
“查看工作流”菜单激活目前本书中我们已经使用过的默认的工作流视图编辑界面。“查看取消处理程序”激活到另一个工作流取消视图,我们可在其中为“取消”处理程序书写代码(见图7-2)。“查看错误处理程序”激活到工作流异常视图(见图7-3)。
图7-2 工作流取消视图设计器界面
图7-3 工作流异常视图设计器界面
你随时可能需要通过一些活动名称下面的智能标记来访问附加设计界面,如图7-4。但有些活动,像EventHandlingScope活动(第十章),你还可访问到更多的界面。
图7-4通过智能标记进行界面的切换
没有什么奇怪的,你拖拽到取消设计界面上的工作流活动在该工作流实例被取消时执行。这使你有机会在工作流实例真正停止执行前去执行一些清理或通知的任务。
错误处理设计界面可容纳许多的错误处理。每一个错误处理可处理一种,也仅能处理一种异常类型。组合活动一般而言都包含错误处理,假如需要的话也允许子活动进行处理错误而不用把它们发送到父活动中。回头看看图7-3,你可看到外面围着蓝色圆圈的两个箭头按钮。错误处理活动可拖到这两个箭头之间,这些箭头允许你进行滚动以显示屏幕之外的错误处理活动。箭头按钮下面的区域是另一个和活动相关联的异常处理的工作流设计界面。通常是拖拽一个Code活动到这里为你做些在错误情况下的清理工作或执行其它必要的处理。在更多地了解工作流的调试视图设计界面后我们将对此做一些练习。
调试视图设计器
你或许不知道你可在工作流的视图设计器上设置断点。其实,在你的工作流中你还能一个活动一个活动地单步执行(而不是在你的源代码中一行一行地执行)。
创建一个使用了Sequence活动的工作流
1.下载本章的源代码,本例的最终版本在“Sequencer Completed”目录下,可使用Visual Studio 2008打开并直接查看它的运行结果。“Sequencer”目录下则为练习版本,我们将从该版本开始本例的学习,首先使用Visual Studio 2008打开该解决方案。
2.在我们的解决方案中添加一个顺序工作流库的项目,项目名称为“SequencerFlow”。
3.从工具箱中拖拽一个Sequence活动到Visual Studio的工作流视图设计器上。
4.然后,从工具箱中拖拽一个Code活动到我们刚添加的Sequence活动中。
5.在该活动的ExecuteCode属性中输入“DoTaskOne”,然后按下回车键。
6.Visual Studio会自动把我们带到代码编辑状态。定位到Visual Studio刚刚添加的DoTaskOne方法,然后再该方法内输入下面的代码:
Console.WriteLine("Executing Task One...");
7.反复执行步骤4、5、6两次,添加方法“DoTaskTwo”和“DoTaskThree”,然后在这些方法中修改Console.WriteLine输出的内容(“One”依次改为“Two”、“Three”)。该工作流的视图设计器现如下图所示: