Workflow筆記3——BookMark和持久化


BookMark

我們在平時的工作流使用中,並不是直接這樣一氣呵成將整個工作流直接走完的,通常一個流程到了某一個節點,該流程節點的操作人,可能並不會馬上去處理該流程,而只有當處理人處理了該流程,流程才會繼續往下走。對於不同流程節點的處理人,他所能處理的是不同的流程節點。

怎么讓流程停下來,等待其他用戶對流程進行參與處理。並且必須能夠保證流程能夠在原有的處理現場情況進行保存,而且能夠對流程進行繼續啟動和處理?那就是書簽。

就好像我們看書,我們需要書簽來標識,我現在已經看到哪個地方了,工作流也是一樣的,我需要通過書簽,來確定不同角色的人能處理的是哪一個流程。

要定義具備Bookmark的Activit,可從 NativeActivity繼承,override [Execute 方法],使用[NativeActivityContext.CreateBookmark方法]添加Bookmark,需要override [ CanInduceIdle 屬性],使其返回值為[True]。

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、運行結果如下:

工作流持久化

持久化:工作流持久性是指獨立於進程或計算機信息持續捕獲工作流實例的狀態。持久存儲化,例如用磁盤進行存儲,光盤存儲等持久化的存儲數據就是持久化。

為何持久化?財務審批中,提交了審批邀請后,財務總監過了一周才進行審批。那么數據必須進行持久化的保存,等待流程的繼續相關處理。
工作流在長時間運行時難免會遇到一些問題,許多業務邏輯需要花費數日、數周乃至數月。在這段時間中,我們不能讓工作流實例一直駐留在內存中。

工作流什么時候進行持久化?

  • 當 TransactionScope 活動完成時或 TransactedReceiveScope 活動完成時。
  • 當工作流實例變為空閑狀態,且對工作流主機設置了 WorkflowIdleBehavior 時。 例如,當使用消息傳遞活動或 Delay 活動時會發生此情況。
  • 當 WorkflowApplication 變為空閑狀態且將應用程序的 PersistableIdle 屬性設置為 PersistableIdleAction.Persist 時。
  • 當指示主機應用程序持久化或卸載工作流實例時。
  • 當終止工作流實例或工作流實例結束時。
  • 當執行 Persist 活動時。
  • 當使用 Windows Workflow Foundation 的早期版本開發的工作流實例在可互操作執行過程中遇到持久點時。

1、通過創建一個數據庫來持久保存工作流實例。新建數據庫WorkFlowDB:

CREATE DATABASE [WorkFlowDB]
 CONTAINMENT = NONE
 ON  PRIMARY 
( NAME = N'WorkFlowDB', FILENAME = N'G:\DataBase\WorkFlowDB.mdf' , SIZE = 5120KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
 LOG ON 
( NAME = N'WorkFlowDB_log', FILENAME = N'G:\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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM