首页 > 代码库 > Workflow笔记3——BookMark和持久化

Workflow笔记3——BookMark和持久化

BookMark

我们在平时的工作流使用中,并不是直接这样一气呵成将整个工作流直接走完的,通常一个流程到了某一个节点,该流程节点的操作人,可能并不会马上去处理该流程,而只有当处理人处理了该流程,流程才会继续往下走。对于不同流程节点的处理人,他所能处理的是不同的流程节点。

就好像我们看书,我们需要书签来标识,我现在已经看到哪个地方了,工作流也是一样的,我需要通过书签,来确定不同角色的人能处理的是哪一个流程。

1、在项目WindowsWorkFlowApp中,新建“代码活动” BookMarkCodeActivity

修改继承类为NativeActivity,Execute方法的参数类型变为NativeActivityContext类型了。代码如下:

    public sealed class BookMarkCodeActivity : NativeActivity    {        // 定义一个字符串类型的活动输入参数        public InArgument<string> BookMarkName { get; set; }        //定义一个输出参数,用来做流程判断,相当于模拟用户处理流程节点的操作        public OutArgument<int> Num { get; set; }        // 创建一个BookMark,让流程停下来        protected override void Execute(NativeActivityContext context)        {            // 1.获取BookMark名称            string strBookMarkName = context.GetValue(BookMarkName);            // 2.创建BookMark            context.CreateBookmark(strBookMarkName,new BookmarkCallback(PreExecuteWorkFlow));        }        /// <summary>        /// 注意,一定要记得注意重写此属性,并返回true,否则后面运行会报错        /// </summary>        protected override bool CanInduceIdle        {            get            {                return true;// base.CanInduceIdle;            }        }        /// <summary>        /// 继续执行下一个状态前,必须先执行该方法。        /// </summary>        /// <param name="context"></param>        /// <param name="bookmark">书签</param>        /// <param name="value">传递过来的值</param>        public void PreExecuteWorkFlow(NativeActivityContext context, Bookmark bookmark, object value)        {            context.SetValue(Num, Convert.ToInt32(value));        }}

2、生成项目WindowsWorkFlowApp

3、双击State1打开,将代码活动添加到State1中,并创建变量Vnum。

技术分享

4、创建输入参数InputBookMarkName

技术分享

5、改造Form1窗体

技术分享

修改启动工作流的代码:

将以WorkflowApplication app;提取到类下面。

            app = new WorkflowApplication(new Activity1(), new Dictionary<string, object>() {             {"InputName","神刀张三"},{"InputBookMarkName",txtBookMarkName.Text}            });            app.Idle = delegate(WorkflowApplicationIdleEventArgs er)            {                Console.WriteLine("工作流 {0} 空闲.", er.InstanceId);                syncEvent.Set(); //这里要唤醒,不让的话,当创建了一个书签之后,界面就卡死了。            };

为“继续执行”按钮添加代码

        //唤醒BookMark执行流程        private void btnContinue_Click(object sender, EventArgs e)        {            //这里会调用PreExecuteWorkFlow方法,并将txtNum的值传过去            app.ResumeBookmark(txtBookMarkName.Text, int.Parse(txtNum.Text));        }

6、双击T1进行修改,添加条件判断

技术分享

假设VNum变量的值等于5,则继续往下执行State2。

7、添加T3,当VNum变量的值不等于5,再回到State1。

技术分享

双击T3,添加条件

技术分享

8、运行结果如下:

技术分享

工作流持久化

1、通过创建一个数据库来持久保存工作流实例。新建数据库WorkFlowDB:

CREATE DATABASE [WorkFlowDB] CONTAINMENT = NONE ON  PRIMARY ( NAME = NWorkFlowDB, FILENAME = NG:\DataBase\WorkFlowDB.mdf , SIZE = 5120KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB ) LOG ON ( NAME = NWorkFlowDB_log, FILENAME = NG:\DataBase\WorkFlowDB_log.ldf , SIZE = 2048KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)GO

2、然后新建表来存储工作流的实例数据,如何新建表?

到%WINDIR%\Microsoft.NET\Framework\v4.xxx\SQL\EN 文件夹下面去寻找脚本,按Win+R,运行%WINDIR%\Microsoft.NET\Framework

技术分享技术分享

找到这两个SQL脚本之后,在数据库WorkFlowDB中首先运行 SqlWorkflowInstanceStoreSchema.sql 文件,然后运行 SqlWorkflowInstanceStoreLogic.sql 文件。执行完成之后,就会在数据库WorkFlowDB中新建如下表。

技术分享

InstancesTable表就是用来存储工作流实例的表。

3、在项目WindowsWorkFlowApp中,添加如下两个程序集的引用

技术分享

4、修改工作流启动代码

引入命名空间

using System.Activities.DurableInstancing;  

修改btnStartWorkFlow_Click代码:

            SqlWorkflowInstanceStore store =    new SqlWorkflowInstanceStore(@"Server=.\MSSQLSERVER2012;database=WorkFlowDB;uid=sa;pwd=yujie1127);            app.InstanceStore = store;

只需要这两行代码,就可以执行持久化工作。那么当下次重新打开工作流的时候,我需要从数据库中找到是那一条工作流实例数据,为了演示简单,我这里就将工作流实例的主键直接放到From窗体界面展示,而通常在工作中,我们是会用数据表来专门存储这些数据信息的。

5、改造Form1代码,修改btnContinue_Click

技术分享
using System;using System.Activities;using System.Collections.Generic;using System.Threading;using System.Windows.Forms;using System.Activities.DurableInstancing;  namespace WindowsWorkFlowApp{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        static readonly string ConnStr=@"Server=.\MSSQLSERVER2012;database=WorkFlowDB;uid=sa;pwd=yujie1127";        //WorkflowApplication app;        AutoResetEvent syncEvent = new AutoResetEvent(false);        private void btnStartWorkFlow_Click(object sender, EventArgs e)        {            WorkflowApplication app = new WorkflowApplication(new Activity1(), new Dictionary<string, object>() {             {"InputName","神刀张三"},{"InputBookMarkName",txtBookMarkName.Text}            });            SqlWorkflowInstanceStore store =    new SqlWorkflowInstanceStore(ConnStr);            app.InstanceStore = store;            txtID.Text = app.Id.ToString();            WorkFlowEvent(app, syncEvent);            app.Run();            syncEvent.WaitOne();        }        private static void WorkFlowEvent(WorkflowApplication app, AutoResetEvent syncEvent)        {            #region 工作流生命周期事件            app.Unloaded = delegate(WorkflowApplicationEventArgs er)            {                Console.WriteLine("工作流 {0} 卸载.", er.InstanceId);            };            app.Completed = delegate(WorkflowApplicationCompletedEventArgs er)            {                Console.WriteLine("工作流 {0} 完成.", er.InstanceId);                syncEvent.Set();            };            app.Aborted = delegate(WorkflowApplicationAbortedEventArgs er)            {                Console.WriteLine("工作流 {0} 终止.", er.InstanceId);            };            app.Idle = delegate(WorkflowApplicationIdleEventArgs er)            {                Console.WriteLine("工作流 {0} 空闲.", er.InstanceId);                syncEvent.Set(); //这里要唤醒,不让的话,当创建了一个书签之后,界面就卡死了。            };            app.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs er)            {                Console.WriteLine("持久化");                return PersistableIdleAction.Unload;            };            app.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs er)            {                Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",       er.InstanceId, er.UnhandledException.Message);                return UnhandledExceptionAction.Terminate;            };            #endregion        }        //唤醒BookMark执行流程        private void btnContinue_Click(object sender, EventArgs e)        {            #region old code            //这里会调用PreExecuteWorkFlow方法,并将txtNum的值传过去            //app.ResumeBookmark(txtBookMarkName.Text, int.Parse(txtNum.Text));             #endregion            WorkflowApplication app = new WorkflowApplication(new Activity1());            SqlWorkflowInstanceStore store =    new SqlWorkflowInstanceStore(ConnStr);            app.InstanceStore = store;            WorkFlowEvent(app, syncEvent);            app.Load(Guid.Parse(txtID.Text)); //加载工作流实例            //继续执行此工作流实例            app.ResumeBookmark(txtBookMarkName.Text, int.Parse(txtNum.Text));         }    }}
View Code

技术分享

6、我们看数据表中已经多了一条工作流实例数据

技术分享

7、然后关闭应用程序,再重新启动

从数据库中找到这个ID,然后填写上。

技术分享

我们看到整个工作流执行完成了,在来看数据表中的工作流实例数据已经删除了。

技术分享

源码下载:WorkflowConsoleApp3.zip

Workflow笔记3——BookMark和持久化