我們在平時的工作流使用中,並不是直接這樣一氣呵成將整個工作流直接走完的,通常一個流程到了某一個節點,該流程節點的操作人,可能並不會馬上去處理該流程,而只有當處理人處理了該流程,流程才會繼續往下走。對於不同流程節點的處理人,他所能處理的是不同的流程節點。
就好像我們看書,我們需要書簽來標識,我現在已經看到哪個地方了,工作流也是一樣的,我需要通過書簽,來確定不同角色的人能處理的是哪一個流程。
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 = 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 Code6、我們看數據表中已經多了一條工作流實例數據
7、然後關閉應用程序,再重新啟動
從數據庫中找到這個ID,然後填寫上。
我們看到整個工作流執行完成了,在來看數據表中的工作流實例數據已經刪除了。
源碼下載:WorkflowConsoleApp3.zip