事实上,我们在实际应用中的WF,都很难以在几秒钟乃至几分钟内运行完成,很多时候,运行周期可能会长达数天数月,比如一个订单处理工作流。这个时候,让线程在内存中挂起如此长的时间,是不现实的,第一是占用系统资源,第二是如果中途遇到系统重启等故障,则会造成数据丢失的风险。
因此,WF需要提供一种能让状态持久化的机制。在运行长时间任务时,能让工作流暂时脱机。WF提供了一个工作流持久化的服务:SqlWorkflowPersistenceService,它用来把工作流实例序列化进SQL SERVER数据库。
WorkflowInstance提供了三个方法用来执行持久化。分别是:
Load:加载先前被持久化的实例
Unload:从内存中卸载(持久化)该工作流实例,该方法会阻塞当前线程,直到该工作流实例被真正卸载,这可以是一个漫长的过程。
TryUnload:从内存中卸载(持久化)该工作流实例,该方法不会阻塞当前线程,不管工作流实例被卸载与否,都会立即返回。
下面以一个示例来说明实现WF持久化的详细步骤:
创建SQL SERVER数据库以进行持久化。
首先在SQL SERVER 2008中创建一个新的数据库,名称为:WorkflowStore。
然后在路径:C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\ZH-CHS中找到SqlPersistenceService_Schema.sql和SqlPersistenceService_Logic.sql。在数据库WorkflowStore中运行这这段脚本。脚本会创建SqlWorkflowPersistenceService需要用到的数据表以及存储过程。
创建宿主应用程序
1、打开VS2008,创建一个WINDOWS应用程序。名称为APJ.WF.WorkflowPersister。
图1
2、在项目中添加应用程序配置文件app.config。在app.config中加入数据库连接字符串。如下所示:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="StorageDatabase" connectionString="Data Source=apj092;Initial Catalog=WorkflowStore;Integrated Security=True;"/>
</connectionStrings>
</configuration>
3、打开Form1.cs切换到WINDOWS Forms试图设计器。在窗体上放入三个按钮,分别更改显示的名称如下图所示:

图2
4、于是我们就为测试工作流加载卸载准备好了界面。接下来我们准备一个顺序工作流。在解决方案中新建一个顺序工作六库,名称为:APJ.WF.PersistedWorkflow

图3
5、在Workflow1.cs中顺序加入两个Code活动。如下图:

图4
6、分别修改其ExcuteCode代码为:
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()
)
);
}
7、再回到宿主程序,Windows Form应用程序。在应用里添加该工作流项目的引用。为Form1.cs类添加两个全局的似有变量_runtime,_instance 分别为WF运行时对象和WF实例对象。在Form1_Load事件中添加如下代码,初始化此二对象。其中用到的部分类和方法我们将在后面添加。
_runtime = WorkflowFactory.GetWorkflowRuntime();
_runtime.WorkflowCompleted +=
new EventHandler<WorkflowCompletedEventArgs>(Runtime_WorkflowCompleted);
_runtime.WorkflowTerminated +=
new EventHandler<WorkflowTerminatedEventArgs>(Runtime_WorkflowTerminated);
8、分别修改三个button的Click代码为如下所示:
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
}
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;
}
此三个button的作用为控制工作流的启动,卸载和加载。
9、在Form1.cs的类的末尾加入如下代码:
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
}
10、在项目中添加WF工厂类,取名为:WorkflowFactory此类的作用为使用工厂和单例模式提供一个唯一的WF的运行时对象。具体代码如下:
class WorkflowFactory
{
//workflow runtime的单一实例
private static WorkflowRuntime _workflowRuntime = null;
//private static object _syncRoot = new object();
//工厂方法
public static WorkflowRuntime GetWorkflowRuntime()
{
if (null == _workflowRuntime)
{
AppDomain.CurrentDomain.ProcessExit += new EventHandler(StopWorkflowRuntime);
AppDomain.CurrentDomain.DomainUnload += new EventHandler(StopWorkflowRuntime);
_workflowRuntime = new WorkflowRuntime();
string conn = ConfigurationManager.ConnectionStrings["StorageDatabase"].ConnectionString;
_workflowRuntime.AddService(new SqlWorkflowPersistenceService(conn));
_workflowRuntime.StartRuntime();
}
return _workflowRuntime;
}
static void StopWorkflowRuntime(object sender, EventArgs e)
{
if (_workflowRuntime != null)
{
if (_workflowRuntime.IsStarted)
{
try
{
_workflowRuntime.StopRuntime();
}
catch (ObjectDisposedException)
{
}
}
}
}
}
现在整个示例就做好了。全部代码在本文的末尾提供下载。我们按F5运行,可以看到如下的效果:

图5
点击Start WF之后,在10秒内点击Unload WF ,一阵无响应(少于10秒)之后,可以在数据库中打开InstanceState表,可以看到这样一条记录。可见,此工作流已经被持久化到数据库中了。
图6
在适当的时候,又可以通过重新加载此工作流,恢复WF的状态。
全部代码点击此处下载。
APJ.WF.WorkflowPersister.rar (59.29 kb)