您的位置:首页 > 其它

WF从入门到精通(第六章):加载和卸载实例 (转)

2011-01-27 23:40 525 查看
学习完本章,你将掌握:
1.理解工作流实例为什么要卸载和重新加载及其时机
2.理解工作流实例为什么要持久化及其时机
3.搭建SQL Server 2005,使其为WF和工作流持久化提供支持
4.使用SqlWorkflowPersistenceService服务
5.在你的工作流代码中进行实例的加载和卸载
6.使持久化服务能自动地加载工作流实例及卸载空闲中的工作流实例

假如你花点时间真正考虑该怎样使用WF和工作流在你的应用程序中进行处理的话,你或许想像的许多解决方案中都包含那些需长时间运行的处理过程。毕竟,商业软件本质上就是模拟和执行业务处理过程,这些许多的处理过程中都包含人或厂商、订货和发货、计划安排等等。人没有在几毫秒内自动进行处理的响应能力,但在已加载的企业服务器上则能做到点。服务器是宝贵、繁忙的资源,需让它进行线程的轮转,让线程等待几分钟、几小时甚至几天、几周是不能接受的,原因有很多。
因此WF的设计器必须提供一个机制,当等待一些长期运行的任务时,空闲工作流应暂时脱机。WF决定提供Microsoft SQL Server作为一个可选的存储介质,因为数据库是储存(而不是失去)宝贵数据的好地方。WF也集成了另一个可插拔服务,我们可轻易地把它纳入我们的工作流中以支持其持久化机制。怎么做到这些、为什么做、什么时候做是我们在本章中将探讨的问题。

持久化工作流实例

你知道现代Microsoft Windows操作系统本质上是一个非常特别的批处理软件,它负责为各请求线程分配占用处理器的时间吗?假如一个单一的线程独占了处理器一个过份的时间周期,其它线程会“饿死”,系统将会死锁。因此这个批处理软件,也就是任务调度程序,要把线程从处理器的执行堆栈上移进和移除,以便所有的线程都能得到执行时间。
从某个意义上说,工作流也类似。假如你有许许多多长时间运行的工作流,它们都挂到某一特定的计算机上竞争处理时间和资源的话,那么系统最终会阻塞未处理的工作流。这就没有了可伸缩性。事实上,WF在维护它的工作流队列时是非常高效的,但你可能对这些必须有一个物理上的上限表示赞同,把空闲、长期运行的工作流从激活执行状态移除是一个好主意。
或者发生什么意外,系统忽然被关闭呢?工作流完全在内存中处理,除非我们采取步骤持久化它们。因此,除非我们在突发事件发生之前有所准备,否则我们就将丢失了执行中的工作流实例。假如那些长期运行的工作流正管理着关键的进程,它们丢失了我们能承受得起吗?在大多数情况下,我们都承受不起,至少我们不会心甘情愿地允许这些进程在毫无防备措施的情况下就消失掉。
好消息是WF不仅为您提供了卸载工作流实例,然后又重新加载的方法,它也支持一个服务:SqlWorkflowPersistenceService,它用来把工作流实例序列化进SQL Server数据库。假如你读过前面一章,你可能已经熟悉甚至对把工作流实例的信息写入数据库的主张感到满意。
因此,在什么时侯卸载工作流实例,并且假如他们被卸载,我们应怎么持久化它们呢?在执行中有一些非常特别的点可卸载工作流实例。在大多数情况下,发生这种情况是在我们会自动为刚才我之所以提到的——WF不能离开(或不愿离开)长期运行的工作流程,在内存中不必要地消耗资源和处理时间的时候。但我们也能亲自进行控制。这里列出了工作流实例的卸载点,在那些地方可进行持久化。
1.在ActivityExecutionContext完成并结束(卸载)后。我们在第四章(活动类型和工作流类型介绍)简要谈过ActivityExecutionContext对象。
2.在Activity进入空闲状态时。
3.一旦一个TransactionScopeActivity完成(卸载)时。我们将在第十五章(工作流和事务)看到这个活动。
4.一旦一个带有PersistOnCloseAttribute属性的Activity完成。
5.当你明确调用WorkflowInstance.Unload或WorkflowInstance.TryUnload时。
通过调用WorkflowInstance对象的特定方法或通过使用一个Delay活动让你的工作流进入空闲状态,你可控制工作流实例被持久化的时机。在延时的时候,通过传递一个参数到持久化服务的构造函数中,你将可对自动的持久化行为进行控制。
备注:暂停工作流实例和对工作流实例进行延时是不相同的。使用Delay活动将自动地把工作流实例写入数据库(前提是你正使用SqlWorkflowPersistenceService并已经对其进行配置,本章的最后一节将看到这些)。暂停仅仅是使工作流从激活的处理状态中撤出。然而你也可选择使用Unload或TryUnload以手动的方式把工作流写入到数据库中。
WF是如何完成这些的呢?这通过使用SqlWorkflowPersistenceService并结合创建一个特定的数据库来完成这项任务(这非常像我们在前一章创建的跟踪数据库)。你可使用相关脚本来创建一个数据库的架构(表和视图)以及执行持久化所需的存储过程。首先让我们来创建数据库。

搭建SQL Server以进行持久化

就像前一章一样,我们通过在SQL Server Management Studio Express中创建一个新数据库来开始我们的工作。
创建一个SQL Server 2005持久化数据库
1.启动SQL Server Management Studio,连接数据库引擎。
<?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>
3.当你创建了WorkflowPersister项目时,Visual Studio显示了Windows Forms视图设计器。在Windows Forms视图设计器中把鼠标移到工具箱上,选择一个Button控件,并把它拖放到设计器的界面上。
4.我们将为这个按钮设置一些富有意义的文字属性,以便于我们知道我们点击的是什么。选中这个按钮,然后在Visual Studio的属性面板中选择该按钮的Text属性,把该属性的值设置为“Start Workflow”。
5.为该按钮添加Click事件的处理程序,具体代码将在后面的步骤中添加。
6.修改按钮的位置和大小,如下图所示:
_runtime = WorkflowFactory.GetWorkflowRuntime();
_runtime.WorkflowCompleted +=
new EventHandler<WorkflowCompletedEventArgs>(Runtime_WorkflowCompleted);
_runtime.WorkflowTerminated +=
new EventHandler<WorkflowTerminatedEventArgs>(Runtime_WorkflowTerminated);
10。在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”按钮是不可使用的。这样做的意义是:假如卸载请求失败,也就不用再加载。

button2.Enabled = false;
try
{
_instance.Unload();
button3.Enabled = true;
} // try
catch (Exception ex)
{
MessageBox.Show(String.Format("Exception while unloading workflow" +
" instance: '{0}'",ex.Message));
} // catch123

备注:牢记WorkflowInstance.Unload是同步的,虽然我在本章前面已经阐述过,但这点很重要。这意味着线程试图卸载工作流实例时将会被阻塞(暂停),直到操作完成后为止(不管卸载实例时是成功还是失败)。在这种情况下,可准确执行我想要的行为(指卸载),因为我不想反复查看实例是否被卸载。但有时,你想在卸载时不会被阻塞,就应使用前面说过的Workflowinstance.TryUnload。稍后,在你添加完最后一批代码并运行应用程序时,当你单击“Unload Workflow”时密切观察,你会看到应用程序会简短地被冻结,因为它正等待工作流卸载。
14.现在回到我们关注的工作流事件处理上:Runtime_WorkflowCompleted和Runtime_WorkflowTerminated。这两个事件处理实际上完成相同的动作,那就是重置应用程序以便为另一个工作流实例的执行做好准备。在“button2”的“click”事件处理方法(该方法包含代码我们已在前面的步骤中添加)的下面添加下面这些方法:

void Runtime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)

void Runtime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)

15.当然,我们现在还要创建“WorkflowCompleted”方法。假如你熟悉Windows编程,你可能知道Windows一直以来存在着的限制问题。这个限制简单的说就是你不能在创建窗口控件的线程外的任何线程中改变该窗口控件的状态。因此假如你想改变某个控件的text,你必须在创建该控件的同一线程上指定该控件的text,使用任何其它线程最有可能导致的结果是造成你的应用程序崩溃。因此我们即将加入的代码对你来说可能很搞笑,但所有要做的这些的真正目的是确信我们是在原来的创建按钮的线程中来使按钮能用和禁用。(事件处理几乎总是在不同的线程上进行调用。)假如我们只是在按钮自身的事件处理中使其可用,应用程序可能还是能工作,但它更大的可能是崩溃和挂起。简单地把下面的代码复制并放到源文件Form1类的尾部,应用程序才会正确的工作。

private delegate void WorkflowCompletedDelegate();

private void WorkflowCompleted()
{
if (this.InvokeRequired)
{
// Wrong thread, so switch to the UI thread
WorkflowCompletedDelegate d = delegate() { WorkflowCompleted(); };
this.Invoke(d);
} // if
else
{
button1.Enabled = true;
button2.Enabled = false;
button3.Enabled = false;
} // else
}

16.在创建我们将执行的工作流之前我们需要最后做一件事,那就是要修改WorkflowFactory类。假如你从第五章(“为你的工作流添加跟踪服务”)以来准确地遵循了所有的步骤来创建和修改WorkflowFactory的话,你实际上创建了一个为工作流运行时提供跟踪服务的工厂对象。我们将对该代码进行轻微的调整,把SqlTrackingService服务改为SqlWorkingPersistenceService,并且改变声明命名空间的语句(把System.Workflow.Runtime.Tracking改为System.Workflow.Runtime.Hosting)。打开WorkflowFactory.cs文件进行编辑。
17.用下面的代码来替换声明的System.Workflow.Runtime.Tracking命名空间。
using System.Workflow.Runtime.Hosting;
using System.Configuration;
18。最后为运行时添加持久化服务,方法是在创建工作流运行时对象后添加下面的代码:

string conn = ConfigurationManager.ConnectionStrings["StorageDatabase"].ConnectionString;
_workflowRuntime.AddService(new SqlWorkflowPersistenceService(conn));

注意:因为我们在代码中创建的工作流实例的类型是PersistedWorkflow.Workflow1类型(在第12步中),因此我们的宿主应用程序编译并执行会出错,我们将在下面的一节解决。
这里我们就有了一个Windows图形用户界面和宿主应用程序,我们使用它们来承载我们的工作流。谈到工作流,我们不是要创建并执行它吗?其实,这在下面进行讲解。
创建一个新的可卸载的工作流
1.像前面一章一样,我们又将在我们的项目中创建一个新的顺序工作流库。在Visual Studio中激活WorkflowPersister应用程序,然后点击“文件”菜单,选择“添加”,当子菜单弹出后,选择“新建项目”。从“添加新项目”的对话框中添加一个“顺序工作流库”的项目,项目名称为“PersistedWorkflow”。
2.在应用程序解决方案中创建并添加一个新项目后,将呈现该工作流的视图设计器界面。从工具箱中拖拽一个“Code”活动到设计器界面上。在Visual Studio属性面板上设置“Code”活动的“ExecuteCode”属性为PreUnload然后按下回车键。
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

4.为了计算工作流消耗的时间(下面的步骤中我们将看到,该时间至少在两个Code活动执行的时间之间),我在一个名称为“_started”字段中保存了启动时间。在你的源文件中构造函数的上面添加该字段:
private DateTime _started = DateTime.MinValue;
5.现在切换到设计视图,添加第二个Code活动。该活动的ExecuteCode属性设置为“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秒届满后,该工作流将被卸载并持久化到数据库中。这些事一旦发生,你就可喝杯咖啡、吃吃棒棒糖或者其它任何事休息休息:你的工作流已被保存到数据库中,正等待再次加载。让我们看看这又是怎么工作的。

加载实例

WorkflowInstance公开了二个卸载方法:“Unload”和“TryUnload”,但仅仅只有一个“Load”方法,该方法不用关心工作流实例是怎样存储到数据库中的。一旦它(工作流实例)被存储,你就可使用WorkflowInstance.Load来把它再次重置到执行状态。现在我们将向WorkflowPersister应用程序中添加合适的代码来做这些事情。
加载被持久化的工作流
1.在Visual Studio中打开WorkflowPersister应用程序,打开该源代码文件,找到主应用程序窗体,定位到“button3_Click”事件处理方法。
2.在“button3_Click”事件处理中添加下面的代码:

button3.Enabled = false;
try
catch (Exception ex)

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表,会在该表中看到一行记录,这行就是你的被持久化的工作流实例!
NameValueCollection parms = new NameValueCollection();
parms.Add("UnloadOnIdle", "true");
parms.Add("ConnectionString", ConfigurationManager.ConnectionStrings["StorageDatabase"].ConnectionString);
_workflowRuntime.AddService(new SqlWorkflowPersistenceService(parms));
4.和前面的例子一样添加一个应用程序配置文件(连接字符串仍然相同)。具体过程可参考第五章中的“为你的工作流添加跟踪服务”,那里添加了该app.config文件。
5.创建一个单独的顺序工作流库项目来承载我们的新工作流,工作流库的名称为IdledWorkflow。
6.重复前一个名称为“创建一个新的可卸载的工作流”例子中的步骤2到步骤4。这些步骤放置了二个Code活动到你的工作流中。
7.在源代码文件中添加下面的代码到“PreUnload”方法中(先前一步你已添加了“PostUnload”方法的代码)。

_started = DateTime.Now;
System.Diagnostics.Trace.WriteLine(
String.Format("*** Workflow {0} started: {1}",
WorkflowInstanceId.ToString(),
_started.ToString("MM/dd/yyyy hh:mm:ss.fff")));

8.返回到工作流视图设计器上,拖拽一个Delay活动到两个code活动之间。
// Create the workflow instance.
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(IdledWorkflow.Workflow1));

// Start the workflow instance.
instance.Start();
13.按F6键编译该解决方案,纠正任何弹出的编译错误。现在,当你执行该WorkflowIdler应用程序时,Delay活动将强制工作流实例持久化到存储数据库中。然而,你会等上超过30秒(多达2分钟)的时间,实例才被重新加载。那是因为工作流运行时在空闲状态下由于延时,要周期性地对持久化工作流进行检查,但它不能保证那些工作流将仅仅等上所期望的延时时间。WF会周期性地轮询数据库,寻找那些正等待计时器事件的空闲并已被持久化的工作流(Delay活动使用了一个计时器)。默认的轮询时间是2分钟。
备注:可以更改默认的数据库轮询时间,方法是使用SqlWorkflowPersistenceService服务时为其提供一个TimeSpan,可使用四个参数的构造函数(分别是连接字符串、工作流处于空闲状态时是否卸载的标志、工作流保持锁定的时间长度以及持久化服务轮询数据库以查找计时器已过期的工作流的频率)。

本章源代码:源码下载(里面包含本章练习项目、完整代码以及相关数据库)

WorkflowIdler.sln

using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Configuration;
using System.Collections.Specialized;

namespace WorkflowIdler
{
public static class WorkflowFactory
{
// Singleton instance of the workflow runtime.
private static WorkflowRuntime _workflowRuntime = null;

// Lock (sync) object
private static object _syncRoot = new object();

// Factory method.
public static WorkflowRuntime GetWorkflowRuntime()
{
// Lock execution thread in case of multi-threaded
// (concurrent) access.
lock (_syncRoot)
{
// Check for startup condition.
if (null == _workflowRuntime)
{
// Provide for shutdown
AppDomain.CurrentDomain.ProcessExit += new
EventHandler(StopWorkflowRuntime);
AppDomain.CurrentDomain.DomainUnload += new
EventHandler(StopWorkflowRuntime);

// Not started, so create instance.
_workflowRuntime = new WorkflowRuntime();

NameValueCollection parms = new NameValueCollection();
parms.Add("UnloadOnIdle", "true");
parms.Add("ConnectionString", ConfigurationManager.ConnectionStrings["StorageDatabase"].ConnectionString);
_workflowRuntime.AddService(new SqlWorkflowPersistenceService(parms));

// Start the runtime.
_workflowRuntime.StartRuntime();
} // if

// Return singleton instance.
return _workflowRuntime;
} // lock
}

// Shutdown method
static void StopWorkflowRuntime(object sender, EventArgs e)
{
if (_workflowRuntime != null)
{
if (_workflowRuntime.IsStarted)
{
try
{
// Stop the runtime
_workflowRuntime.StopRuntime();
}
catch (ObjectDisposedException)
{
// Already disposed of, so ignore...
} // catch
} // if
} // if
}
}
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Runtime;
using System.Threading;

namespace WorkflowIdler
{
class Program
{
private static AutoResetEvent _waitHandle = new AutoResetEvent(false);
static void Main(string[] args)
{
WorkflowRuntime workflowRuntime = WorkflowFactory.GetWorkflowRuntime();
workflowRuntime.WorkflowIdled += new EventHandler<WorkflowEventArgs>(workflowIdled);

workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(workflowCompleted);
workflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(workflowTerminated);

Console.WriteLine("Waiting for workflow completion.");
// Create the workflow instance.
WorkflowInstance instance =
workflowRuntime.CreateWorkflow(typeof(IdledWorkflow.Workflow1));

// Start the workflow instance.
instance.Start();

_waitHandle.WaitOne();
Console.WriteLine("Done.");
Console.ReadLine();
}

static void workflowTerminated(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine("Workflow instance terminated, " +
"reason: '{0}'.", e.Exception.Message);
_waitHandle.Set();
}

static void workflowCompleted(object sender, WorkflowCompletedEventArgs e)
{
Console.WriteLine("Workflow instance completed.");
_waitHandle.Set();
}

static void workflowIdled(object sender, WorkflowEventArgs e)
{
Console.WriteLine("Workflow instance idled.");
}
}
}

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;

namespace IdledWorkflow
{
public sealed partial class Workflow1: SequentialWorkflowActivity
{
public Workflow1()
{
InitializeComponent();
}

private DateTime _started = DateTime.MinValue;

private void PreUnload(object sender, EventArgs e)
{
_started = DateTime.Now; System.Diagnostics.Trace.WriteLine( String.Format("*** Workflow {0} started: {1}", WorkflowInstanceId.ToString(), _started.ToString("MM/dd/yyyy hh:mm:ss.fff")));}

private void PostUnload(object sender, EventArgs e)
{
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()));
}
}

}

¥¥¥¥¥¥¥¥¥¥¥¥¥

WorkflowPersister.sln

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Workflow.Runtime;

namespace WorkflowPersister
{
public partial class Form1 : Form
{
protected WorkflowRuntime _runtime = null;
protected WorkflowInstance _instance = null;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
_runtime = WorkflowFactory.GetWorkflowRuntime();
_runtime.WorkflowCompleted +=
new EventHandler<WorkflowCompletedEventArgs>(Runtime_WorkflowCompleted);
_runtime.WorkflowTerminated +=
new EventHandler<WorkflowTerminatedEventArgs>(Runtime_WorkflowTerminated);
}

private void button1_Click(object sender, EventArgs e)
{
button2.Enabled = true; button1.Enabled = false; _instance = _runtime.CreateWorkflow(typeof(PersistedWorkflow.Workflow1)); _instance.Start();
}

private void button2_Click(object sender, EventArgs e)
{
button2.Enabled = false;
try
{
_instance.Unload();
button3.Enabled = true;
} // try
catch (Exception ex)
{
MessageBox.Show(String.Format("Exception while unloading workflow" +
" instance: '{0}'", ex.Message));
} // catch123
}

void Runtime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e)
{
WorkflowCompleted();
}

void Runtime_WorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
{
WorkflowCompleted();
}

private delegate void WorkflowCompletedDelegate();

private void WorkflowCompleted()
{
if (this.InvokeRequired)
{
// Wrong thread, so switch to the UI thread...
WorkflowCompletedDelegate d = delegate() { WorkflowCompleted(); };
this.Invoke(d);
} // if
else
{
button1.Enabled = true;
button2.Enabled = false;
button3.Enabled = false;
} // else
}

private void button3_Click(object sender, EventArgs e)
{
button3.Enabled = false;
try
{
_instance.Load();
} // try
catch (Exception ex)
{
MessageBox.Show(String.Format("Exception while loading workflow" +
" instance: '{0}'", ex.Message));
} // catch

button1.Enabled = true;
}
}
}

using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Configuration;

namespace WorkflowPersister
{
public static class WorkflowFactory

{
// Singleton instance of the workflow runtime
private static WorkflowRuntime _workflowRuntime = null;

// Lock (sync) object
private static object _syncRoot = new object();

// Factory method.
public static WorkflowRuntime GetWorkflowRuntime()
{
// Lock execution thread in case of multi-threaded
// (concurrent) access.
lock (_syncRoot)
{
// Check for startup condition.
if (null == _workflowRuntime)
{
// Provide for shutdown
AppDomain.CurrentDomain.ProcessExit +=
new EventHandler(StopWorkflowRuntime);
AppDomain.CurrentDomain.DomainUnload +=
new EventHandler(StopWorkflowRuntime);

// Provide for shutdown
AppDomain.CurrentDomain.ProcessExit += new
EventHandler(StopWorkflowRuntime);
AppDomain.CurrentDomain.DomainUnload += new
EventHandler(StopWorkflowRuntime);

// Not started, so create instance.
_workflowRuntime = new WorkflowRuntime();

string conn = ConfigurationManager.ConnectionStrings["StorageDatabase"].ConnectionString; _workflowRuntime.AddService(new SqlWorkflowPersistenceService(conn));

// Start the runtime
_workflowRuntime.StartRuntime();

} // if

// Return singleton instance.
return _workflowRuntime;
} // lock
}

// Shutdown method
static void StopWorkflowRuntime(object sender, EventArgs e)
{
if (_workflowRuntime != null)
{
if (_workflowRuntime.IsStarted)
{
try
{
// Stop the runtime
_workflowRuntime.StopRuntime();
}
catch (ObjectDisposedException)
{
// Already disposed of, so ignore...
} // catch
} // if
} // if
}

}
}

using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Workflow.Runtime;
using System.Threading;

namespace WorkflowPersister
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
private static AutoResetEvent _waitHandle = new AutoResetEvent(false);

[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());

WorkflowRuntime workflowRuntime = WorkflowFactory.GetWorkflowRuntime();
workflowRuntime.WorkflowIdled += new EventHandler<WorkflowEventArgs>(workflowIdled);
workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(workflowCompleted);
workflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(workflowTerminated);

Console.WriteLine("Waiting for workflow completion.");
_waitHandle.WaitOne();
Console.WriteLine("Done.");

}

static void workflowTerminated(object sender, WorkflowTerminatedEventArgs e)
{
Console.WriteLine("Workflow instance terminated, reason: '{0}'.", e.Exception.Message);
_waitHandle.Set();
}

static void workflowCompleted(object sender, WorkflowCompletedEventArgs e)
{
Console.WriteLine("Workflow instance completed.");
_waitHandle.Set();
}

static void workflowIdled(object sender, WorkflowEventArgs e)
{
Console.WriteLine("Workflow instance idled.");
}
}
}

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Drawing;
using System.Linq;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;

namespace PersistedWorkflow
{
public sealed partial class Workflow1: SequentialWorkflowActivity
{
private DateTime _started = DateTime.MinValue;

public Workflow1()
{
InitializeComponent();
}

private void PreUnload(object sender, EventArgs e)
{
_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}

private void PostUnload(object sender, EventArgs e)
{
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()));
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐