一 概述
在没有持久化之前,流程实例是存在于内存中的,所以我们最多只能有一个宿主来使用这个流程实例,一旦流程包含多个节点,需要多次使用这个流程实例,那么流程示例的持久化就是不可或缺的一个过程。Workflow4将工作流持久化封装成一个插件来提供持久化服务,并且通过调用SqlWorkflowPersistenceService类来实现SQL Server数据中流程实例的持久化。
二 工作原理
工作流提供了持久化服务,我们只需要通过开启持久化服务即可实现流程实例的持久化。在初始化工作流宿主时同时初始化流程实例,然后我们就可以在工作流生命周期中的特定时间来进行流程实例的持久化。这里的指定时间有以下几种情况:
1 工作流空闲时;
2 工作流完成或终止时;
3 活动TransactionScopeActivity/CompensatableTransactionScopeActivity/CompensatableSequenceActiviy 完成时;
4 自定义活动完成时;
5 调用WorkflowInstance的一种方法,且这种方法又会导致持久化操作时。
对于开发者来说,不需要额外去控制持久化服务,流程实例的持久化是由工作流引擎自动控制的。
三 数据模型
1 添加System.Activities.DurableInstancing 和 System.Runtime.DurableInstancing的引用。
2 新建数据库countersignatureDB;运行C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en下的SqlWorkflowInstanceStoreSchema和SqlWorkflowInstanceStoreLogic脚本,截图如下:

数据库表截图如下:
3 表说明
[System.Activities.DurableInstancing].[InstancesTable] | 实例表 |
[System.Activities.DurableInstancing].[RunnableInstancesTable] | 运行实例表 |
[System.Activities.DurableInstancing].[KeysTable] | 关键字表 |
[System.Activities.DurableInstancing].[LockOwnersTable] | 锁定表 |
[System.Activities.DurableInstancing].[InstanceMetadataChangesTable] | 实例元数据变化表 |
[System.Activities.DurableInstancing].[ServiceDeploymentsTable] | 服务部署表 |
[System.Activities.DurableInstancing].[InstancePromotedPropertiesTable] | 实例属性表 |
[System.Activities.DurableInstancing].[SqlWorkflowInstanceStoreVersionTable] | 实例版本表 |
[System.Activities.DurableInstancing].[Instances] | 实例View |
四 持久化方式
1 比较
我们在前面讲了使用开启持久化服务的方式进行持久化,我们也完全可以使用自定义持久化服务来实现,下面我们对比一下这两种方式。
优点 | 缺点 | |
WF4持久化 | 1、能够比较方便利用现有的建表脚本创建持久化需要的数据库表 2、不用关心流程实例持久化发生的时间,运行时引擎会自动处理 3、不用关心如何实现持久化这些技术细节 4、有现有的服务接口可以调用,可扩展 5、完成对流程实例整个生命周期的管理 6、同WF 4现有的API易于集成和工作 | 1、仅仅使用现有的表是不够的,还需要自定义进行扩展数据库表,如一些设置和参数信息 2、无法控制流程持久化的时间,需要使用持久化的数据时,数据可能并没有被持久化 3、受持久化现有服务和接口的制约 4、需要对现有的一些数据库表进行扩展,这些扩展中的数据和已建表的一些数据要做到同步,增加了事件、事务处理的复杂度 5、开发进度和质量依赖与对SqlWorkflowPersistenceService的掌握程度 6、流程处理的性能需要验证 7、WF4所带数据库是一个黑盒,没有相关的文档说明和数据库设计文档 |
自定义持久化 | 1、能根据用户需求完全自定义数据结构和模型 2、能够控制持久化的时机,对流程数据具有完全的权限 3、降低了流程数据持久化的技术难度 4、统一设计数据模型,减少流程中的事务处理和数据关联关系 5、针对用户需求来进行设计 | 1、需要自行设计数据库模型 2、不能使用WF4 提供现有持久化接口和流程管理与监控的一些功能,这些功能需要自定义开发 3、开发工作量相对较大,但是技术难度会有所降低 |
2 结论
如果我们只是做一些简单的流程,软件比较小,则可选用持久化服务;
如果我们的业务流程比较复杂,功能要求比较多,权限要求比较高,则可选用自定义服务来实现。
五 实现
1 代码
//自定义节点
<span style="font-size:18px;"> class PersistActivity : NativeActivity
{
//节点默认执行的方法
protected override void Execute(NativeActivityContext context)
{
//创建书签,此书签可以让工作流进入Idle状态以进行工作流持久化
context.CreateBookmark("bookmarkTest",new BookmarkCallback(this.Continue));
}
/// <summary>
///重新启动流程时的回调函数
/// </summary>
/// <param name="context">流程上下文</param>
/// <param name="bookmark">流程可以被恢复的点</param>
/// <param name="obj">流程恢复是需要关联的数据</param>
void Continue(NativeActivityContext context, Bookmark bookmark, object obj)
{
}
//设置此节点是否可以被持久化,默认返回值为true
protected override bool CanInduceIdle
{
get
{
return true;
}
}
}
</span>
<pre name="code" class="csharp"><span style="font-size:18px;"> class Program
{
public static AutoResetEvent async = new AutoResetEvent(false);
static void Main(string[] args)
{
//数据库连接字符串
string sqlConnectionString = "server=.;database=countersignatureDB;uid=sa;password=123456";
SqlWorkflowInstanceStore instanceStore = new SqlWorkflowInstanceStore(sqlConnectionString);
InstanceView view = instanceStore.Execute(instanceStore.CreateInstanceHandle(), new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(10));
instanceStore.DefaultInstanceOwner = view.InstanceOwner;
//创建宿主,并设置持久化对象
WorkflowApplication application = new WorkflowApplication(new PersistActivity());
application.InstanceStore = instanceStore;
//给各事件注册方法
application.Idle += workflowIdle;
application.Unloaded += workflowUnload;
application.PersistableIdle += workflowPersistabled;
//数据持久化
application.Persist();
application.Run();
async.WaitOne();
Console.ReadKey();
}
//定义工作流进入空闲状态时执行的事件
static void workflowIdle(WorkflowApplicationIdleEventArgs e)
{
Console.WriteLine("WorkflowIdle" + e.Bookmarks.FirstOrDefault().BookmarkName);
}
//定义工作流进入卸载状态时执行的事件
static void workflowUnload(WorkflowApplicationEventArgs e)
{
Console.WriteLine("WorkflowUnloaded" + System.DateTime.Now.ToString());
//给主线程返回信息,返回到主线程继续执行
async.Set();
}
//定义工作流进入空闲状态并且可被持久化时执行的事件
static PersistableIdleAction workflowPersistabled(WorkflowApplicationIdleEventArgs e)
{
Console.WriteLine("WorkflowPersist" + System.DateTime.Now.ToString());
/*PersistableIdleAction为枚举对象,共三个枚举,None、Unload、Persist
*1 None为宿主不进行任何操作
*2 Persist位宿主保持工作流
*3 Unload为宿主卸载当前工作流,其他宿主就可以重启此工作流了
*/
return PersistableIdleAction.Unload;
}
//定义工作流发生异常时执行的事件
static UnhandledExceptionAction workflowUnhandledException(WorkflowApplicationUnhandledExceptionEventArgs e, UnhandledExceptionAction action)
{
Console.WriteLine("WorkflowException" + e.UnhandledException.Message);
return UnhandledExceptionAction.Terminate;
}
}</span>
2 说明
我们首先在自定义活动中,设置此节点是可以被实例化的;然后添加重启流程的回调函数,此方法可以在流程重启时首先执行,进而处理我们自定义的一些活动;最后创建书签,创建书签可以使工作流运行到此节点时将状态修改为Idle,从而进行持久化。
宿主程序中,我们首先设置数据库连接字符串,然后创建持久化对象SqlWorkflowInstanceStore,并且将连接字符串传入持久化对象;然后我们创建宿主对象,并且将宿主的InstanceStore属性设置为持久化对象来制定使用的持久化存储;接着,我们给流程的各个事件注册我们预先定义好的方法;最后,我们启动流程,并且使用AutoResetEvent对象来实现多线程。
3 程序输出结果
4 数据库数据更新
六 注意事项
1 自定义节点中,必须要设置是否可以被持久化属性,否则不能实现持久化;
2 自定义节点中,必须要设置书签,否则工作不会进入Idle空闲状态,从而进行持久化操作;
3 宿主程序中,一定要手动进行流程持久化存储,也就是说需要显示调用WorkflowApplication.Persist()方法进行数据持久化;
4 必须阻塞主线程,否则主线程执行结束时,创建、运行和持久化流程的从线程不会执行结束,从而不能实现流程持久化;
5 如果想要实现多次使用此流程,则持久化工作流时需要选择PersistableIdleAction.Unload,即保持并卸载工作流方式,此方式可以使当前宿主放弃锁定流程实例,从而可以实现另外的宿主重启此流程。
七 总结
大多数的流程都会用到持久化功能,这个功能目前使用比较成熟,常用的持久化方式包括数据库方式和XML方式,数据库方式小编只实现了SQL Server数据库。
工作流探索刚刚起步