一起学WF3.5【9】

真实的工作流程中,许多处理过程都要花费很长时间。但在服务器上,这些工作流程序如果保持长时间的资源占用,线程等上几分钟、几小时甚至几天,这对有限的服务器资源是不能接受的。因此WF中需要提供将长时间运行且空新的工作流暂时脱机的机制。WF使用数据库作为存储介质,集成了一个可纳入我们的工作流并使其持久化的机制。还有一种原因是工作流完全在内存处理,如果系统突然关闭将会发生意外,需要我们持久化它们。

持久化工作流实例

WF可以为我们加载和卸载工作流实例,它支持一个服务:SqlWorkflowPersistenceService,用来把工作流序列化进SQL Server数据库。

在内存中不必要的消耗资源和处理时间时,工作流需卸载并进行持久化。有如下卸载点:

ActivityExecutionContext完成并结束后;Activity进入空闲状态时;一个TransactionScopeActivity完成时;明确调用WorkflowInstance.Unload或WorkflowInstance.TryUnload时。

 

一个例子

建数据库

通过使用SqlWorkflowPersistenceService并结合创建一个特定的数据库来说明这个过程。像之前一样首先创建数据库。我们使用与上文相同的Windows目录下的SqlPersistenceService_Schema和SqlPersistenceService_Logic两个脚本。首先创建数据库WorkflowStore,在其中执行两个脚本。

关于SqlWorkflowPersistenceService服务

工作流处于运行中时,WorkflowInstance和SqlWorkflowPersistenceService协同工作以执行存储和恢复任务。

经常会用单一的数据库来存储多个在不同机器不同进程上的工作流实例,如果要保存和恢复,需要有一种方法来存储工作流在执行时刻的系统状态,如实例是否被阻塞,它的状态(执行中,空闲等),序列化实例数据和拥有者标识等辅助信息。这些信息在恢复时是必须的。

WorkflowInstance对象有三个方法来控制上述持久化:

方法

功能

Load

加载先前被卸载(持久化)的工作流实例

TryUnload

试图从内存中卸载(持久化)该工作流实例。和调用 Unload不同的是,调用 TryUnload 时假如工作流实例不能立即被卸载,那它将不会被阻塞(维持执行状态)。

Unload

从内存中卸载(持久化)该工作流实例。注意该方法为进行卸载将阻塞当前执行的线程,直到工作流实例被真正地卸载。这可以是一个漫长的操作,这取决于个人的工作流任务。

TryUload和Unload方法视你的代码要求,二者各有优缺点。

 

卸载实例

WF在特定的时间会卸载和持久化工作流实例。如果你想手动控制,使用上述两个方法。但如果没有先插入SqlWorkflowPersistenceService就调用上述两个方法之一,WF会抛异常。如果有数据库错误,也会有异常。因此使用try/catch块来包围这些调用以阻止程序崩溃。

 

我们用一个WinForm程序加以说明。

打开VS新建一个Windows应用程序,名为WindowPersister。

如前几节所述的步骤,进行如下工作:

在主程序中添加工作流引用;创建工作流运行时工厂对象;启动工作流运行时;使用工作流工厂对象;处理工作流运行时事件。

添加一个App.config文件,并添加:

<?xml version="1.0"encoding="utf-8" ?>

<configuration>

<connectionStrings>

<addname="StorageDatabase"connectionString="DataSource=(local)\SQLEXPRESS;Initial Catal

og=WorkflowStore;Integrated Security=True;"/>

</connectionStrings>

</configuration>     

 

设计Form,如下图:

 

我们在主窗体的Load事件添加名为Form1_Load方法,并添加如下代码:

_runtime = WorkflowFactory.GetWorkflowRuntime();

_runtime.WorkflowCompleted +=

newEventHandler<WorkflowCompletedEventArgs>(Runtime_WorkflowCompleted);

_runtime.WorkflowTerminated+=

newEventHandler<WorkflowTerminatedEventArgs>(Runtime_WorkflowTerminated);

加如下字段:

protected WorkflowRuntime _runtime = null;

protected WorkflowInstance _instance = null;

添加 System.Workflow.Runtime、System.Workflow.ComponentModel和System.Workflow.Activity 三个工作流组件的引用,并添加运行时命名空间。

现在我们就有了一个Windows Form来宿主工作流运行时。

向Start Workflow按钮事件添加一些代码:

         button2.Enabled= true;

           button1.Enabled = false;

           _instance = _runtime.CreateWorkflow(typeof(PersistedWorkflow.Workflow1));

           _instance.Start();

启动了工作流实例。

添加Unload WorkflowInstance按钮的事件处理:button2_onclick。我们使用WorkflowInstance.Unload方法来卸载工作流实例并把它写入数据库。卸载完成后,我们让"LoadWorkflow"按钮可用,但如果卸载产生异常,Load Workflow按钮也是不可用的。

接下来回到工作流事件处理上,对完成和中止事件添加相同的代码:

WorkflowCompleted();

这是一个未定义的方法。具体代码如下:

private voidWorkflowCompleted()

{

     if (this.InvokeRequired)

     {

         WorkflowCompletedDelegated = new delegate() { WorkflowCompleted(); };

         this.Invoke(d);

     }

     else

     {

         button1.Enabled = true;

         button2.Enabled = false;

         button3.Enbaled = flase;

     }

}

并添加:private delegate voidWorkflowCompletedDelegate();

上述代码是在工作流停止或中止后让三个按钮变为初始的状态。但是看起来有些费解(对委托不熟的初学者可以借机复习一下),有一点需要注意,即UI线程不允许其他线程对其进行修改,所以上述代码的加入了对当前线程的判断,确保在UI线程上修改按钮状态,否则有可能会产生程序崩溃。

最后我们还要在WorkflowFactory中添加SqlWorkflowPersistenceService,添加命名空间,System.Workflow.Runtime.Hosting。

在工作流运行是对象后添加下如下代码:

string conn = ConfigurationManager.ConnectionStrings["StorageDatabase"].ConnectionString;

_workflowRuntime.AddService(newSqlPersistenceService(conn));

 

到现在我们还缺一个工作流以便在宿主中执行。像上节一样,创建一个新的工作流顺序库。项目名称为PersistedWorkflow。我们向设计器中添加两个Code活动。它们的ExecuteCode属性分别设为PreUnload和PostUnload,从而在Workflow1.cs中生成了这两个方法。

PreUnload中添加:

_started= DateTime.Now;

           System.Diagnostics.Trace.WriteLine(String.Format("*** Workflow {0}started: {1}",

            WorkflowInstanceId.ToString(),

            _started.ToString("MM/dd/yyyyhh:mm:ss.fff")));

            System.Threading.Thread.Sleep(10000);

在PostCode中添加:

DateTimeended = 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/yyyyhh:mm:ss.fff"),

            duration.ToString()));

 

向WorkflowPersister添加工作流的引用。

 

加载实例

一旦工作流被存储,可使用Load方法把它再次重置到执行状态。

在WorkflowPersister的Form1.cs的button3_Click事件处理中添加:

button3.Enabled = false;

try

{

_instance.Load();

} // try

catch(Exception ex)

{

MessageBox.Show(String.Format("Exception whileloading workflow"+

" instance: '{0}'", ex.Message));

} // catch

button1.Enabled = true;

编译,运行。可启动、卸载和加载工作流了。调试时可在输出窗口看到我们输出的文本。

 

空闲时加载和卸载实例

在上面的工作流中我们使用的是System.Threading.Thread.Sleep代替Delay活动。Delay活动执行时,如果工作流已附加了SqlWorkflowPersistenceService服务,该工作流运行时自动地为你持久化该工作流,延时周期过后又恢复它,为了使之能够自动持久化,你要为SqlWorkflowPersistenceService添加一个特定的构造函数参数。构造函数参数能使SqlWorkflowPersistenceService的internal方法UnloadOnIdle在工作流空闲时被调用。下面使用一个集合参数,它包括了连接字符串和空闲卸载标志。

 

我们可使用上例的代码说明,作一些修改。

在WorkflowFactory类里,添加using System.Collections.Specialized;

在运行时对象被创立后添加持久化服务:

NameValueCollection parms = newNameValueCollection();

parms.Add("UnloadOnIdle","true");

parms.Add("ConnectionString",ConfigurationManager.ConnectionStrings["StorageDatabase"].Connecti

onString);

_workflowRuntime.AddService(newSqlWorkflowPersistenceService(parms));

 

在工作流项目中,把最后一句System.Threading.Thread.Sleep删除。在两个Code活动之间加一个Delay活动,TimeoutDuration设置为30秒

编译,运行。


坚持WF(1):从HelloWorld开始 本文主要通过实现了一个可以接受参数的HelloWorld程序来了解WF。 坚持WF(2):WF创作模式和设计时工具 坚持WF(3):WF框架概览 2.3两篇主要全面的阐述了WF框架和Visual Studio对开发WF的一些设计时的工具。 坚持WF(4):活动(Activity)和依赖属性(DependencyProperty) 坚持WF(5):自定义活动(CustomActivity) 4.5两篇对主要对活动(Actibity)的介绍和依赖属性(DependencyObject)和DependencyProperty事件的使用。 坚持WF(6):开发可复用的宿主程序 主要实现两个类来对WorkflowInstance和WorkflowRuntime进行简单的封装。 坚持WF(7):流程控制(Flow Control) 主要说了WF中和流程相关的活动,主要包括以下这些活动:IfElseActivity,WhileActivity,ParallelActivity,ReplicatorActivity,ConditionedActivityGroup,InvokeWorkflowActivity,TerminateActivity,SuspendActivity等。 坚持WF(8):本地服务之调用外部方法 坚持WF(9):本地服务之事件处理 工作流能够使用方法和事件通过消息与宿主程序交互。 事件用于将数据发送到工作流,而工作流使用方法将数据发送到主机应用程序,8.9两篇主要说了本地服务如何调用外部方法和接收事件。 坚持WF(10):在工作流中使用关联 当工作流实例在本地服务接口上侦听相同事件的不同实例时,就无法确定该响应哪个事件。如何解决这个问题呢,我们就需要在工作流中使用关联。 坚持WF(11):工作流通信与队列 WF 提供的通信模型是构建于队列系统的基础之上,我们可以使用自定义活动来注册以接收关于队列的消息,而宿主应用程序中的服务则发送关于队列的消息。自定义活动可以使用此模型来处理外部事件,也可以传递异步活动执行的完成。这样,您的活动可以先执行到某一点,然后等待激发因素的到来以便继续执行。 坚持WF(12):使用EventHandlingScopeActivity活动 EventHandlingScopeActivity活动包含一个主线子活动和一组事件处理活动(EventHandlersActivity),它的主线子活动中只能包含一个子活动。它会执行其主子活动比如SequenceActivity。 同时,可能会执行每个EventDrivenActivity,也可能不执行它们,这取决于在SequenceActivity 活动执行时是否发生了它们的事件,当主线活动执行完了整个EventHandlingScopeActivity活动也就结束了。 坚持WF(13):WF中的持久化服务 主要介绍了如何使用WF中提供的SqlWorkflowPersistenceService来进行持久化存储。 坚持WF(14):自定义持久化服务 我们除了使用WF提供的SqlWorkflowPersistenceService外,还可以自定义持久化服务。因为有的时候你可能不想使用Sql Server数据库,我们就可以通过自定义持久化服务来使用其他的数据库,文件等来进行持久化存储。 坚持WF(15):状态机工作流 本文主要介绍WF中状态机工作流的基础知识,状态机工作流和顺序型工作流不同。顺序型工作流一般是比较固定的,可预测的,和系统交互的时候比较多。而状态机工作流一般是不可预测,和人的交互会比较多一些,一般有回退流程的时候使用状态机工作流会比较好一点,如何正确的选择顺序型工作流还是状态机工作流是十分重要的。 坚持WF(16):WF中的事务 在关系型数据库中支持事务已经有10几年了,事务用来解决数据的完整性、一致性等等问题。在WF中提供了TransactionScopeActivity活动用来支持事务,当该活动执行时一个System.Transactions.Transaction实例就被创建了,如果TransactionScopeActivity中的子活动有一个有异常,就会执行回滚操作。 坚持WF(17):WF中的补偿 事务是你在做一组更新时要不成功,要不失败,不可能只部分更新。事务最经典的使用场合就是在关系型数据库中。补偿是取消之前已经成功的操作,在我们的工作流中我们可能完成一项任务有很多活动组成,比如我们的活动都使用了TransactionScopeActivity,我们在之后发现错误了,想要取消之前成功的操作,这个和事务就没有关系了,因为事务已经成功了。这个时候我们就可以使用补偿。 坚持WF(18):使用IPendingWork接口 WF会定期在各个持久性点(Persistence Point)检查并将工作流实例保存到持久化存储中,这样如果工作流出现错误或是异常终止时相关的信息就会被存储,下次加载工作流实例时就会从此做为开始点。这个主要是针对工作内置的服务。工作流的外部服务使用工作批次来保留状态信息。 这些服务对其工作项进行批处理并共享与工作流的事务相同的事务。 如果工作流尚未提交事务,则服务仍可以在持久点期间对某些工作项进行批处理。WF提供 IWorkBatch 和 IPendingWork 以帮助服务和工作流实例保留状态信息。 坚持WF(19):工作流线程 WF中提供了很多内置的服务,其中工作流计划服务是用来管理工作流实例线程的。默认情况下WF会自动使用DefaultWorkflowSchedulerService服务,我们还可以手动去加载ManualWorkflowSchedulerService服务。本文利用MSDN中的一个例子来说明工作流中的线程以及如何使用DefaultWorkflowSchedulerService和ManualWorkflowSchedulerService服务。 坚持WF(20):规则引擎中的活动条件 坚持WF(21):规则引擎中的RuleSet 坚持WF(22):跟踪规则 坚持WF(23):以代码的方式构造和执行RuleSet WF提供了非常灵活,强大和简单易懂的规则引擎。这几篇文章介绍了WF中规则引擎的相关知识。 坚持WF(24):WF中的异常处理 坚持WF(25):取消容器(CancellationHandlerActivity) 说明了WF如果捕获和处理异常,并介绍了CancellationHandlerActivity活动的基本使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值