首页 > 编程开发 > Objective-C编程 >
-
坚持学习WF13 WF中持久化服务
工作流系统中有很大一部分需要和人进行交互,有的时候需要很长的时间,所以我们不可能让工作流实例一直保存在内存当中,这就需要进行持久化操作。工作流的持久化就是保存工作流的某些状态信息到持久化存储里,比如sql数据库,文件中,一旦被保存到持久化存储里了,工作流就可以从内存中移除掉,在需要的时候在进行装载。
插一句,在Lotus Notes中由于他的设计和数据都是以文档的形式存储在一个nsf中的,所以他天生就对持久化支持,只要我们做简单的做一个FileSave或是Call doc.Save(True,True)就会被持久存储,也正是如此,我们很多关于工作流的状态信息我们都存储在每个数据文档本身当中,可在.Net,Java这些当中却还需要花费很大的力气和DB等交互才可以完成,两种不同风格的开发对比起来确实挺有意思。
持久化服务是WF中核心服务之一,WF框架提供一个标准的持久化服务SqlWorkflowPersistenceService,利用它可以将工作流状态信息存放在Sql Server数据库中,你也可以自己实现持久化服务,你必须继承自WorkflowPersistenceService这个类,自定义的持久化服务可以把数据存储在二进制文件,xml,其他关系型数据库中等等,但是一个工作流实例一次只能使用一个持久化服务。
一旦你将持久化服务加载到工作流引擎中,你就不需要手动去干涉了,他会自动的完成相应的操作。在以下状态的时候,工作流会被持久化。
1. idle的时候(如等待外部事件,使用DelayActivity)。
2. 工作流完成或终止。
3. 当TransactionScopeActivity完成的时候。
4. 当CompensatableSequenceActivity完成的时候。
5. 当一个装饰有PersistOnCloseAttribute的自定义活动完成的时候。
6. 当你手动的去调用Unload或TryUnload方法的时候。
如果你使用DelayActivity的时候,持久化服务也会存储DelayActivity的过期时间,并且持久化服务会定期检查是否过期,以准备从新恢复工作流状态,SqlWorkflowPersistenceService有一个LoadingInterval属性可以设置获取加载间隔的长度。
有的时候当工作流变成idle的时候,你也可以选择不进行持久化存储,这种情况适合当你等待的外部事件比较频繁,而且事件很快就被接收的时候,因为这个时候如果你在进行持久化存储,你花在卸载和装载上时间会更多,还不如不进行持久化存储呢。
下面我们完成一个例子来说如何使用SqlWorkflowPersistenceService:
1.首先我们需要建立我们的持久化数据库,WF已提供了响应sql脚本,位置如下:
[WindowsFolder]Microsoft.NetFrameworkv3.0Windows Workflow FoundationSQL[language].[windows]这里面有SqlPersistenceService_Schema.sql和SqlPersistenceService_Logic.sql两个文件用来生成持久化数据库.
创建好的持久化数据库里包含两张表, InstanceState和 CompletedScope,InstanceState表中记录未完成事例的运行状态,CompletedScope表记录当工做流使用事务的支持。
2.定义两个事件
2.1.首先定义IPersistenceDemo接口:
[ExternalDataExchange]
public interface IPersistenceDemo
{
event EventHandler<ExternalDataEventArgs> ContinueReceived;
event EventHandler<ExternalDataEventArgs> StopReceived;
}
2.2.实现本地服务PersistenceDemoService:
public class PersistenceDemoService : IPersistenceDemo
{
public event EventHandler<ExternalDataEventArgs> ContinueReceived;
public event EventHandler<ExternalDataEventArgs> StopReceived;
public void OnContinueReceived(ExternalDataEventArgs args)
{
if (ContinueReceived != null)
{
ContinueReceived(null, args);
}
}
public void OnStopReceived(ExternalDataEventArgs args)
{
if (StopReceived != null)
{
StopReceived(null, args);
}
}
}
3.实现工作流,如下图:
3.1.whileActivity的条件为!
3.2.heeContinue和heeStop分别接收我们定义的ContinueReceived和StopReceived事件。
代码如下:
public sealed partial class PersistenceDemoWorkflow: SequentialWorkflowActivity
{
private Boolean isComplete = false;
public Boolean IsComplete
{
get { return isComplete; }
set { isComplete = value; }
}
public PersistenceDemoWorkflow()
{
InitializeComponent();
}
private void handleStopReceived_Invoked(object sender, ExternalDataEventArgs e)
{
//tell the WhileActivity to stop
isComplete = true;
}
}
4.实现宿主程序
4.1.首先增加一个类来显示工作流状态
public class Workflow
{
private Guid instanceId = Guid.Empty;
private String statusMessage = String.Empty;
private Boolean isCompleted;
public Guid InstanceId
{
get { return instanceId; }
set { instanceId = value; }
}
public String StatusMessage
{
get { return statusMessage; }
set { statusMessage = value; }
}
public Boolean IsCompleted
{
get { return isCompleted; }
set { isCompleted = value; }
}
}
4.2.这次我们使用的是一个windows form的程序,完成的效果如下:
当我们创建一个工作流实例后,我们看到他的StatusMessage从Created-Ilded-Persisted-Unloaded变化,这是由于工作流执行到ListenActivity中等待事件的时候,期状态会变成ilded,当ilded的时候工作流实例的状态信息将被持久化存储到Sql数据中,状态从Persisted变成Unloaded,这个时候即使你关闭程序也没有关系,如果你点击继续按钮,StatusMessage的变化为Loaded-Ilded-Persisted-Unloaded,他会从新从数据库中装载被持久化的工作流,点击停止后会变成Completed状态。
持久化后我们关闭应用程序在重写打开的时候会调用如下方法来加载所有的工作流实例,主要使用了GetAllWorkflows来获所有工作流实例。
private void RetrieveExistingWorkflows()
{
workflows.Clear();
foreach (SqlPersistenceWorkflowInstanceDescription workflowDesc
in ((SqlWorkflowPersistenceService)persistence).GetAllWorkflows())
{
Workflow workflow = new Workflow();
workflow.InstanceId = workflowDesc.WorkflowInstanceId;
workflow.StatusMessage = "Unloaded";
workflows.Add(workflow.InstanceId, workflow);
}
if (workflows.Count > 0)
{
RefreshData();
}
}
在宿主程序我们定义如下方法来加载SqlWorkflowPersistenceService和本地服务,SqlWorkflowPersistenceService类的一些成员方法的使用差MSDN吧,
private void AddServices(WorkflowRuntime instance)
{
//use the standard SQL Server persistence service
String connStringPersistence = String.Format("Initial Catalog={0};Data Source={1};Integrated Security={2};", "WorkflowPersistence", @"localhostSQLEXPRESS", "SSPI");
persistence = new SqlWorkflowPersistenceService(connStringPersistence, true, new TimeSpan(0, 2, 0), new TimeSpan(0, 0, 5));
instance.AddService(persistence);
//add the external data exchange service to the runtime
ExternalDataExchangeService exchangeService = new ExternalDataExchangeService();
instance.AddService(exchangeService);
//add our local service
persistenceDemoService = new PersistenceDemoService();
exchangeService.AddService(persistenceDemoService);
}
其他的代码基本都是围绕着实现StatusMessage自动变化的,和工作流关系不大。
本文示例源代码或素材下载
6. 当你手动的去调用Unload或TryUnload方法的时候。
如果你使用DelayActivity的时候,持久化服务也会存储DelayActivity的过期时间,并且持久化服务会定期检查是否过期,以准备从新恢复工作流状态,SqlWorkflowPersistenceService有一个LoadingInterval属性可以设置获取加载间隔的长度。
有的时候当工作流变成idle的时候,你也可以选择不进行持久化存储,这种情况适合当你等待的外部事件比较频繁,而且事件很快就被接收的时候,因为这个时候如果你在进行持久化存储,你花在卸载和装载上时间会更多,还不如不进行持久化存储呢。
下面我们完成一个例子来说如何使用SqlWorkflowPersistenceService:
1.首先我们需要建立我们的持久化数据库,WF已提供了响应sql脚本,位置如下:
[WindowsFolder]Microsoft.NetFrameworkv3.0Windows Workflow FoundationSQL[language].[windows]这里面有SqlPersistenceService_Schema.sql和SqlPersistenceService_Logic.sql两个文件用来生成持久化数据库.
创建好的持久化数据库里包含两张表, InstanceState和 CompletedScope,InstanceState表中记录未完成事例的运行状态,CompletedScope表记录当工做流使用事务的支持。
2.定义两个事件
2.1.首先定义IPersistenceDemo接口:
[ExternalDataExchange]
public interface IPersistenceDemo
{
event EventHandler<ExternalDataEventArgs> ContinueReceived;
event EventHandler<ExternalDataEventArgs> StopReceived;
}
2.2.实现本地服务PersistenceDemoService:
public class PersistenceDemoService : IPersistenceDemo
{
public event EventHandler<ExternalDataEventArgs> ContinueReceived;
public event EventHandler<ExternalDataEventArgs> StopReceived;
public void OnContinueReceived(ExternalDataEventArgs args)
{
if (ContinueReceived != null)
{
ContinueReceived(null, args);
}
}
public void OnStopReceived(ExternalDataEventArgs args)
{
if (StopReceived != null)
{
StopReceived(null, args);
}
}
}