学习完本章后,你将掌握:
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的属性
属性 | 功能 |
IsStarted | 用来指明workflow运行时是否已经启动并准备接受workflow实例。当宿主调用“StartRuntime”前IsStarted为False。期间它一直维持True直到宿主调用“StopRuntime”为止。需注意的是当它正在运行中你不能增加核心服务。 |
Name | 获取或设置和WorkflowRuntime关联的名字。Workflow运行时正在运行中你不能设置这个属性(也就是说当IsStarted为True)。企图这样做的结果就是抛出一个“InvalidOperationException”异常。 |
表2-2 WorkflowRuntime的方法
方法 | 功能 |
AddService | 为workflow运行时添加指定的服务。能添加的服务类型和时间受到种种限制。关于服务的详细信息将在第五章介绍。 |
CreateWorkflow | 创建一个workflow实例,它包含一些指定(但可选)的参数。假如workflow运行时没有启动,该方法就调用StartRuntime方法。 |
GetWorkflow | 通过 指明workflow实例的标识符(由一个Guid组成)来检索workflow实例。假如这个workflow实例是空闲和持久化保存的,它将被重新加载并执行。 |
StartRuntime | 启动workflow运行时和相关服务,并引发“Started”事件。 |
StopRuntime | 停止workflow运行时和相关服务,并引发“Stoped”事件。 |
创建一个Workflow运行时工厂
单例和工厂设计模式的组合是强大的,因为工厂能保证只创建出一个曾创建的对象的单一实例,这正好符合我们的要求(在这里使用单例模式的原因主要是从效率上考虑,其次一个应用程序域也只能只有一个WorkflowRuntime),因为WorkflowRuntime完全有可能在不同的应用当中加载和启动(例如独立的应用模块)。让我们看看怎样创建一个WorkflowRuntime工厂。
一、在项目中添加一个类型为类的新项,文件名为WorkflowFactory.cs。
二、在WorkflowFactory.cs源文件中添加如下的引用
using System.Workflow.Runtime;
三、在类中添加下面的代码:
private static WorkflowRuntime _workflowRuntime = null;
private static object _syncRoot = new object();
四、在上述代码后添加如下方法:
public static WorkflowRuntime GetWorkflowRuntime()
{
//多线程环境下防止并发访问
lock (_syncRoot)
{
if (null == _workflowRuntime)
_workflowRuntime = new 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事件处理函数:
{
if (_workflowRuntime != null)
{
if (_workflowRuntime.IsStarted)
{
try
{
_workflowRuntime.StopRuntime();
}
catch (ObjectDisposedException)
{
}
}
}
}
以下是WorkflowFactory.cs文件的完整源代码,在第五章之前我们不会做更多的改变:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.Runtime;
namespace WorkflowHost
{
public static class WorkflowFactory
{
//workflow runtime的单一实例
private static WorkflowRuntime _workflowRuntime = null;
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 _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实例完成后激发。 |
WorkflowIdled | 当一个workflow实例进入空闲状态时激发。当workflow实例进入了空闲状态后,你就有机会把他们从内存中卸载掉、存储到数据库并可在稍后的时间把它们加载进内存。 |
WorkflowTerminated | 当一个workflow实例被终止后激发。在宿主中调用一个workflow实例的Terminate方法、或通过一个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的代码下添加以下代码。
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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.Runtime;
using System.Threading;
namespace 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_WorkflowIdled);
workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(workflowRuntime_WorkflowCompleted);
workflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(workflowRuntime_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