WWF提供的持续化功能会自动记录工作流实例以及它包含的所有活动的执行状态,这些状态并不是指工作流上流转的表单所呈现的业务逻辑状态。WWF持续化功能就是将未执行完成的工作流实例以及该实例中各种活动的状态,以文件或数据库方式进行存储,待需要的时候再重新将其加载回工作流运行时容器Runtime中。运行完毕才删除。
在具体操作中通过"SqlWorkflowPersistenceService"类来实现持续化的功能,基于数据库,其他数据库需重新实现接口。
一、创建SqlPersistenceService数据库
要在SQLServer实现工作流的保存、恢复功能,需要创建一些相关表与存储过程。默认情况下在C:\Windows\Microsoft.NET\Framework\v3.5\SQL\EN 路径下有SqlPersistenceProviderSchema.sql和SqlPersistenceProviderLogic.sql两个文件,打开SQLServer创建一个名叫SqlPersistenceService的数据库,按顺序执行以上两个sql脚本。此时,SqlPersistenceService数据库就创建了几个表与存储过程。
如果实在不行,就把SqlPersistenceService数据库附加进来。https://files.cnblogs.com/kissdodog/WWF%E6%8C%81%E7%BB%AD%E5%8C%96%E6%95%B0%E6%8D%AE%E5%BA%93.rar
接口如下:
[ExternalDataExchange] //using System.Workflow.Activities; public interface IEvent { event EventHandler<ExternalDataEventArgs> PMApproved; event EventHandler<ExternalDataEventArgs> VPApproved; event EventHandler<ExternalDataEventArgs> StaffSubmit; }
新建一个工作流如下:
里面是3个HandleExternalEvent活动,分别于刚刚定义的接口事件相绑定。
在bin目录里面添加一个XML文件,其代码如下:
<WorkflowInstances />
新建一个Winform程序,界面如下:
代码如下:
public partial class Form1 : Form, ClassLibrary1.IEvent { //创建一个WinForm引用程序,然后继承并实现接口中定义的事件 public event EventHandler<ExternalDataEventArgs> PMApproved; public event EventHandler<ExternalDataEventArgs> VPApproved; public event EventHandler<ExternalDataEventArgs> StaffSubmit; private WorkflowRuntime runtime; //SQLServer中的"SqlPersistenceService"数据库是用来存储工作流实例及其活动的运行状态。 //业务逻辑状态也能够以XML文件形式进行存储,当程序刚运行无流程在运行时,添加一个空xml文件 //名为:workflowInstances.xml直接放在bin目录 const string instanceFilename = "workflowInstances.xml"; WorkflowInstance instance; public Form1() { InitializeComponent(); this.runtime = new WorkflowRuntime(); runtime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(runtime_WorkflowCompleted); runtime.WorkflowIdled += OnWorkflowIdled; //应用程序启动时以"SqlWorkflowPersistenceService"为对象来实例化"WorkflowPersistenceService"类, //并通过"SqlWorkflowPersistenceService"来连接SQLServer数据库,同时将工作流的持续化服务加载到工作流运行时容器Runtime中。 WorkflowPersistenceService persistenceService = new SqlWorkflowPersistenceService( "server=CZZ;database=SqlPersistenceService;uid=sa;pwd=123"); runtime.AddService(persistenceService); ExternalDataExchangeService dataService = new ExternalDataExchangeService(); runtime.AddService(dataService); dataService.AddService(this); LoadWorkflowData(); runtime.StartRuntime(); } //在工作流空闲状态时并将其从内存中卸载出去 //重载工作流的"WorkflowIdled"事件,当工作流处于等待状态下时将通过该事件来调用工作流实例的"TryUnload()"方法, //使它激发工作流的"WorkflowPersisted"事件,这样工作流就可以通过"WorkflowPersistenceService"服务将信息自动保存到SQL Server数据库,以实现持续化功能。 static void OnWorkflowIdled(object sender, WorkflowEventArgs e) { e.WorkflowInstance.TryUnload(); } //当工作流完成后,将界面列表中的相应数据删除 //当工作流运行结束时还需要对界面列表中的相关信息进行删除,用户可以通过重载工作流的"WorkflowCompleted"事件来实现。 //要注意分清楚,事件是工作流的事件,而删除界面是WinForm程序的事。工作流与应用程序不在同一个线程,因此不能直接调用。这种情况可以通过委托来实现。 void runtime_WorkflowCompleted(object sender, WorkflowCompletedEventArgs e) { RemoveListViewItem remove = new RemoveListViewItem(RemoveListViewItemAsync); Invoke(remove, e.WorkflowInstance.InstanceId); } private delegate void RemoveListViewItem(Guid instanceId); private void RemoveListViewItemAsync(Guid instanceId) { foreach (ListViewItem item in ListViewExisting.Items) { if (item.SubItems[0].Text.Equals(instanceId.ToString())) ListViewExisting.Items.Remove(item); } } //用户提交请假单的按钮事件 private void btnSubmit_Click(object sender, EventArgs e) { instance = runtime.CreateWorkflow(typeof(WorkflowConsoleApplication1.Workflow1)); instance.Start(); StaffSubmit(null, new ExternalDataEventArgs(instance.InstanceId)); ListViewExisting.Items.Add(new ListViewItem(new String[] { instance.InstanceId.ToString(), this.txtDay.Text, "提交请假单" })); } //当窗体关闭时,将列表中请假单的信息保存到XML中 private void Form1_FormClosing(object sender, FormClosingEventArgs e) { SaveWorkflowData(); runtime.Dispose(); } //项目经理审批的按钮 private void btnPMArrpoved_Click(object sender, EventArgs e) { instance = runtime.GetWorkflow(new Guid(ListViewExisting.SelectedItems[0].Text)); PMApproved(null, new ExternalDataEventArgs(instance.InstanceId)); foreach (ListViewItem item in ListViewExisting.Items) { if (item.SubItems[0].Text.Equals(instance.InstanceId.ToString())) item.SubItems[2].Text = "等待VP审批"; } } //副总经理审批的按钮 private void btnVPApproved_Click(object sender, EventArgs e) { instance = runtime.GetWorkflow(new Guid(ListViewExisting.SelectedItems[0].Text)); VPApproved(null, new ExternalDataEventArgs(instance.InstanceId)); foreach (ListViewItem item in ListViewExisting.Items) { if (item.SubItems[0].Text.Equals(instance.InstanceId.ToString())) item.SubItems[2].Text = "审批通过"; } } #region 读写XML的方法 /// <summary> /// 从XML文件中读取已经钝化的工作流实例的信息 /// </summary> public void LoadWorkflowData() { XmlTextReader reader = new XmlTextReader(instanceFilename); try { while (reader.Read()) { if (reader.Name.Equals("WorkflowInstance")) { ListViewExisting.Items.Add( new ListViewItem( new String[] { reader.GetAttribute("InstanceId"), reader.GetAttribute("Day"), reader.GetAttribute("State")})); } } reader.Close(); } catch (FileNotFoundException) { } } /// <summary> /// 保存工作流实例的信息到XML文件中 /// </summary> public void SaveWorkflowData() { XmlTextWriter writer = new XmlTextWriter(instanceFilename, Encoding.Unicode); writer.WriteStartElement("WorkflowInstances"); foreach (ListViewItem item in ListViewExisting.Items) { writer.WriteStartElement("WorkflowInstance"); writer.WriteAttributeString("InstanceId", item.SubItems[0].Text); writer.WriteAttributeString("Day", item.SubItems[1].Text); writer.WriteAttributeString("State", item.SubItems[2].Text); writer.WriteEndElement(); } writer.WriteEndElement(); writer.Flush(); writer.Close(); } #endregion }
执行效果如下:
由于在关闭窗口的时候,会将当前工作流的执行状态保存到XML文件和数据库中,而下次打开的时候,回去读取上次执行到的状态,所以当工作流执行到某一步关闭窗口再打开,会显示上次保存的状态。