如何建立及執行長時間執行的工作流程
Windows Workflow Foundation (WF) 的其中一項核心功能,就是執行階段能夠將閒置的工作流程保存及卸載至資料庫。 操作說明:執行工作流程中的步驟示範使用主控台應用程式裝載工作流程的基本概念。 範例包括啟動工作流程、工作流程開發週期處理常式,以及繼續使用書籤。 為有效示範工作流程持續性,必須要有較複雜的工作流程主機,以支援啟動與繼續使用多個工作流程執行個體。 教學課程中的這個步驟,示範如何建立 Windows 表單主應用程式,以支援啟動與繼續使用多個工作流程執行個體、工作流程持續性,並且為後續教學課程步驟中示範的追蹤和版本設定等進階功能提供基礎。
注意
此教學課程步驟和後續步驟會使用操作說明:建立工作流程中所有的三種工作流程型別。
若要建立持續性資料庫
開啟 SQL Server Management Studio 並連線至本機伺服器,例如 .\SQLEXPRESS。 以滑鼠右鍵按一下本機伺服器的 [資料庫] 節點,然後選取 [新增資料庫]。 將新資料庫命名為 WF45GettingStartedTutorial,接受所有其他值,然後選取 [確定]。
注意
建立資料庫之前,請確定您有本機伺服器的建立資料庫權限。
從 [檔案] 功能表中選擇 [開啟]、[檔案]。 瀏覽至下列資料夾:C:\Windows\Microsoft.NET\Framework\v4.0.30319\sql\en
選取下列兩個檔案,然後按一下 [開啟]。
SqlWorkflowInstanceStoreLogic.sql
SqlWorkflowInstanceStoreSchema.sql
從 [視窗] 功能表中選擇 SqlWorkflowInstanceStoreSchema.sql。 請務必選取 [可用的資料庫] 下拉式清單中的 [WF45GettingStartedTutorial],然後從 [查詢] 功能表中選擇 [執行]。
從 [視窗] 功能表中選擇 SqlWorkflowInstanceStoreLogic.sql。 請務必選取 [可用的資料庫] 下拉式清單中的 [WF45GettingStartedTutorial],然後從 [查詢] 功能表中選擇 [執行]。
警告
務必按照正確順序執行前面的兩個步驟。 如果未按照正確順序執行查詢,會發生錯誤,而且也無法正確地設定持續性資料庫。
將參考加入至 DurableInstancing 組件
以滑鼠右鍵按一下方案總管中的 NumberGuessWorkflowHost,並選取 [新增參考]。
從 [新增參考] 清單中選取 [組件],並在 [搜尋組件] 方塊中輸入
DurableInstancing
。 如此會篩選組件,讓您更容易選取所需的參考。從 [搜尋結果] 清單中勾選 System.Activities.DurableInstancing 和 System.Runtime.DurableInstancing 旁的核取方塊,然後按一下 [確定]。
建立工作流程主表單
在方案總管中,以滑鼠右鍵按一下 NumberGuessWorkflowHost,然後選擇 [新增]、[新增項目]。
在 [已安裝] 範本清單中選擇 [Windows 表單],在 [名稱] 方塊中輸入
WorkflowHostForm
,然後按一下 [新增]。設定表單中的下列屬性。
屬性 值 FormBorderStyle FixedSingle MaximizeBox False 大小 400, 420 依指定順序將下列控制項加入到表單中,並依指示設定屬性。
控制 屬性:值 按鈕 名稱:NewGame
位置:13、13
大小:75、23
文字:新遊戲標籤 位置:94、18
文字:猜號碼,從 1 到ComboBox 名稱:NumberRange
DropDownStyle:DropDownList
項目:10、100、1000
位置:228、12
大小:143、21標籤 位置:13、43
文字:工作流程型別ComboBox 名稱:WorkflowType
DropDownStyle:DropDownList
項目:StateMachineNumberGuessWorkflow、FlowchartNumberGuessWorkflow、SequentialNumberGuessWorkflow
位置:94、40
大小:277、21標籤 名稱:WorkflowVersion
位置:13、362
文字:工作流程版本GroupBox 位置:13、67
大小:358、287
文字:遊戲注意
新增下列控制項時,將其放入 GroupBox 中。
控制 屬性:值 標籤 位置:7、20
文字:工作流程執行個體識別碼ComboBox 名稱:InstanceId
DropDownStyle:DropDownList
位置:121、17
大小:227、21標籤 位置:7、47
文字:猜謎TextBox 名稱:Guess
位置:50、44
大小:65、20按鈕 名稱:EnterGuess
位置:121、42
大小:75、23
文字:輸入猜測按鈕 名稱:QuitGame
位置:274、42
大小:75、23
文字:退出TextBox 名稱:WorkflowStatus
位置:10、73
Multiline:True
ReadOnly:True
ScrollBars:垂直
大小:338、208將表單的 [AcceptButton] 屬性設定為 [EnterGuess]。
下列範例示範完成的表單。
加入表單的屬性和 Helper 方法
本節中的步驟會將設定表單 UI 的屬性和 Helper 方法加入到表單類別中,以支援執行及繼續使用數字猜測工作流程。
以滑鼠右鍵按一下 [方案總管] 中的 [WorkflowHostForm],然後選擇 [檢視程式碼]。
將下列
using
(或Imports
) 陳述式加入至檔案最上方的其他using
(或Imports
) 陳述式。Imports System.Activities Imports System.Activities.DurableInstancing Imports System.Data.SqlClient Imports System.IO Imports System.Windows.Forms
using System.Activities; using System.Activities.DurableInstancing; using System.Data.SqlClient; using System.IO; using System.Windows.Forms;
將下列成員宣告新增至 [WorkflowHostForm] 類別。
重要
Microsoft建議您使用可用的最安全驗證流程。 如果您要連接到 Azure SQL,Azure 資源受控識別是建議的驗證方法。
Const connectionString = "Server=.\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI" Dim store As SqlWorkflowInstanceStore Dim workflowStarting As Boolean
const string connectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI"; SqlWorkflowInstanceStore store; bool workflowStarting;
注意
如果您的連接字串不同,請更新
connectionString
以參考您的資料庫。將
WorkflowInstanceId
屬性加入至WorkflowFormHost
類別。Public ReadOnly Property WorkflowInstanceId() As Guid Get If InstanceId.SelectedIndex = -1 Then Return Guid.Empty Else Return New Guid(InstanceId.SelectedItem.ToString()) End If End Get End Property
public Guid WorkflowInstanceId { get { return InstanceId.SelectedIndex == -1 ? Guid.Empty : (Guid)InstanceId.SelectedItem; } }
InstanceId
下拉式方塊會顯示持續性工作流程執行個體識別碼的清單,且WorkflowInstanceId
屬性會傳回目前選取的工作流程。加入表單
Load
事件的處理常式。 若要新增處理常式,請切換至表單的 [設計檢視],按一下 [屬性] 視窗最上方的 [事件] 圖示,然後按兩下 [載入]。Private Sub WorkflowHostForm_Load(sender As Object, e As EventArgs) Handles Me.Load End Sub
private void WorkflowHostForm_Load(object sender, EventArgs e) { }
將下列程式碼新增至
WorkflowHostForm_Load
。' Initialize the store and configure it so that it can be used for ' multiple WorkflowApplication instances. store = New SqlWorkflowInstanceStore(connectionString) WorkflowApplication.CreateDefaultInstanceOwner(store, Nothing, WorkflowIdentityFilter.Any) ' Set default ComboBox selections. NumberRange.SelectedIndex = 0 WorkflowType.SelectedIndex = 0 ListPersistedWorkflows()
// Initialize the store and configure it so that it can be used for // multiple WorkflowApplication instances. store = new SqlWorkflowInstanceStore(connectionString); WorkflowApplication.CreateDefaultInstanceOwner(store, null, WorkflowIdentityFilter.Any); // Set default ComboBox selections. NumberRange.SelectedIndex = 0; WorkflowType.SelectedIndex = 0; ListPersistedWorkflows();
當表單載入時,會設定
SqlWorkflowInstanceStore
,範圍和工作流程型別下拉式方塊會設為預設值,而且持續性工作流程執行個體會加入至InstanceId
下拉式方塊。加入
SelectedIndexChanged
的InstanceId
處理常式。 若要新增處理常式,請切換至表單的 [設計檢視],選取InstanceId
下拉式方塊,按一下 [屬性] 視窗最上方的 [事件] 圖示,然後按兩下 [SelectedIndexChanged]。Private Sub InstanceId_SelectedIndexChanged(sender As Object, e As EventArgs) Handles InstanceId.SelectedIndexChanged End Sub
private void InstanceId_SelectedIndexChanged(object sender, EventArgs e) { }
將下列程式碼新增至
InstanceId_SelectedIndexChanged
。 只要使用者使用下拉式方塊選取工作流程,此處理常式就會更新狀態視窗。If InstanceId.SelectedIndex = -1 Then Return End If ' Clear the status window. WorkflowStatus.Clear() ' Get the workflow version and display it. ' If the workflow is just starting then this info will not ' be available in the persistence store so do not try and retrieve it. If Not workflowStarting Then Dim instance As WorkflowApplicationInstance = _ WorkflowApplication.GetInstance(WorkflowInstanceId, store) WorkflowVersion.Text = _ WorkflowVersionMap.GetIdentityDescription(instance.DefinitionIdentity) ' Unload the instance. instance.Abandon() End If
if (InstanceId.SelectedIndex == -1) { return; } // Clear the status window. WorkflowStatus.Clear(); // Get the workflow version and display it. // If the workflow is just starting then this info will not // be available in the persistence store so do not try and retrieve it. if (!workflowStarting) { WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(this.WorkflowInstanceId, store); WorkflowVersion.Text = WorkflowVersionMap.GetIdentityDescription(instance.DefinitionIdentity); // Unload the instance. instance.Abandon(); }
將下列
ListPersistedWorkflows
方法加入至表單類別。Private Sub ListPersistedWorkflows() Using localCon As New SqlConnection(connectionString) Dim localCmd As String = _ "SELECT [InstanceId] FROM [System.Activities.DurableInstancing].[Instances] ORDER BY [CreationTime]" Dim cmd As SqlCommand = localCon.CreateCommand() cmd.CommandText = localCmd localCon.Open() Using reader As SqlDataReader = cmd.ExecuteReader(CommandBehavior.CloseConnection) While (reader.Read()) ' Get the InstanceId of the persisted Workflow. Dim id As Guid = Guid.Parse(reader(0).ToString()) InstanceId.Items.Add(id) End While End Using End Using End Sub
using (var localCon = new SqlConnection(connectionString)) { string localCmd = "SELECT [InstanceId] FROM [System.Activities.DurableInstancing].[Instances] ORDER BY [CreationTime]"; SqlCommand cmd = localCon.CreateCommand(); cmd.CommandText = localCmd; localCon.Open(); using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) { while (reader.Read()) { // Get the InstanceId of the persisted Workflow. Guid id = Guid.Parse(reader[0].ToString()); InstanceId.Items.Add(id); } } }
ListPersistedWorkflows
會在執行個體存放區中查詢持續性工作流成執行個體,並將執行個體識別碼加入cboInstanceId
下拉式方塊。將下列
UpdateStatus
方法及對應的委派加入至表單類別。 此方法會將表單上的狀態視窗更新為目前執行中的工作流程狀態。Private Delegate Sub UpdateStatusDelegate(msg As String) Public Sub UpdateStatus(msg As String) ' We may be on a different thread so we need to ' make this call using BeginInvoke. If InvokeRequired Then BeginInvoke(New UpdateStatusDelegate(AddressOf UpdateStatus), msg) Else If Not msg.EndsWith(vbCrLf) Then msg = msg & vbCrLf End If WorkflowStatus.AppendText(msg) ' Ensure that the newly added status is visible. WorkflowStatus.SelectionStart = WorkflowStatus.Text.Length WorkflowStatus.ScrollToCaret() End If End Sub
private delegate void UpdateStatusDelegate(string msg); public void UpdateStatus(string msg) { // We may be on a different thread so we need to // make this call using BeginInvoke. if (InvokeRequired) { BeginInvoke(new UpdateStatusDelegate(UpdateStatus), msg); } else { if (!msg.EndsWith("\r\n")) { msg += "\r\n"; } WorkflowStatus.AppendText(msg); WorkflowStatus.SelectionStart = WorkflowStatus.Text.Length; WorkflowStatus.ScrollToCaret(); } }
將下列
GameOver
方法及對應的委派加入至表單類別。 當工作流程完成時,此方法會從 [InstanceId] 下拉式方塊中移除已完成之工作流程的執行個體識別碼,以更新表單 UI。Private Delegate Sub GameOverDelegate() Private Sub GameOver() If InvokeRequired Then BeginInvoke(New GameOverDelegate(AddressOf GameOver)) Else ' Remove this instance from the InstanceId combo box. InstanceId.Items.Remove(InstanceId.SelectedItem) InstanceId.SelectedIndex = -1 End If End Sub
private delegate void GameOverDelegate(); private void GameOver() { if (InvokeRequired) { BeginInvoke(new GameOverDelegate(GameOver)); } else { // Remove this instance from the combo box. InstanceId.Items.Remove(InstanceId.SelectedItem); InstanceId.SelectedIndex = -1; } }
設定執行個體存放區、工作流程開發週期處理常式及擴充
將
ConfigureWorkflowApplication
方法加入至表單類別。Private Sub ConfigureWorkflowApplication(wfApp As WorkflowApplication) End Sub
private void ConfigureWorkflowApplication(WorkflowApplication wfApp) { }
此方法會設定
WorkflowApplication
、加入所需的擴充,然後加入工作流程開發週期事件的處理常式。在
ConfigureWorkflowApplication
中指定SqlWorkflowInstanceStore
的WorkflowApplication
。' Configure the persistence store. wfApp.InstanceStore = store
// Configure the persistence store. wfApp.InstanceStore = store;
接下來,建立
StringWriter
執行個體,並將其加入到Extensions
的WorkflowApplication
集合中。StringWriter
新增至延伸模組後,會擷取所有WriteLine
活動輸出。 工作流程閒置時,可以從WriteLine
擷取StringWriter
輸出並顯示在表單上。' Add a StringWriter to the extensions. This captures the output ' from the WriteLine activities so we can display it in the form. Dim sw As New StringWriter() wfApp.Extensions.Add(sw)
// Add a StringWriter to the extensions. This captures the output // from the WriteLine activities so we can display it in the form. var sw = new StringWriter(); wfApp.Extensions.Add(sw);
加入
Completed
事件的下列處理常式。 當工作流程成功完成時,會在狀態視窗中顯示用來猜測數字的次數。 如果工作流程終止,會顯示導致終止的例外狀況資訊。 在處理常式結束時,會呼叫GameOver
方法,此方法會移除工作流程清單中已完成的工作流程。wfApp.Completed = _ Sub(e As WorkflowApplicationCompletedEventArgs) If e.CompletionState = ActivityInstanceState.Faulted Then UpdateStatus($"Workflow Terminated. Exception: {e.TerminationException.GetType().FullName}{vbCrLf}{e.TerminationException.Message}") ElseIf e.CompletionState = ActivityInstanceState.Canceled Then UpdateStatus("Workflow Canceled.") Else Dim turns As Integer = Convert.ToInt32(e.Outputs("Turns")) UpdateStatus($"Congratulations, you guessed the number in {turns} turns.") End If GameOver() End Sub
wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e) { if (e.CompletionState == ActivityInstanceState.Faulted) { UpdateStatus($"Workflow Terminated. Exception: {e.TerminationException.GetType().FullName}\r\n{e.TerminationException.Message}"); } else if (e.CompletionState == ActivityInstanceState.Canceled) { UpdateStatus("Workflow Canceled."); } else { int turns = Convert.ToInt32(e.Outputs["Turns"]); UpdateStatus($"Congratulations, you guessed the number in {turns} turns."); } GameOver(); };
加入下列
Aborted
和OnUnhandledException
處理常式。 不會從GameOver
處理常式呼叫Aborted
方法,因為當工作流程執行個體中止時,並沒有終止,稍後可以再繼續該執行個體。wfApp.Aborted = _ Sub(e As WorkflowApplicationAbortedEventArgs) UpdateStatus($"Workflow Aborted. Exception: {e.Reason.GetType().FullName}{vbCrLf}{e.Reason.Message}") End Sub wfApp.OnUnhandledException = _ Function(e As WorkflowApplicationUnhandledExceptionEventArgs) UpdateStatus($"Unhandled Exception: {e.UnhandledException.GetType().FullName}{vbCrLf}{e.UnhandledException.Message}") GameOver() Return UnhandledExceptionAction.Terminate End Function
wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e) { UpdateStatus($"Workflow Aborted. Exception: {e.Reason.GetType().FullName}\r\n{e.Reason.Message}"); }; wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e) { UpdateStatus($"Unhandled Exception: {e.UnhandledException.GetType().FullName}\r\n{e.UnhandledException.Message}"); GameOver(); return UnhandledExceptionAction.Terminate; };
加入下列
PersistableIdle
處理常式。 此處理常式會擷取所加入的StringWriter
擴充,從WriteLine
活動擷取輸出,並顯示在狀態視窗中。wfApp.PersistableIdle = _ Function(e As WorkflowApplicationIdleEventArgs) ' Send the current WriteLine outputs to the status window. Dim writers = e.GetInstanceExtensions(Of StringWriter)() For Each writer In writers UpdateStatus(writer.ToString()) Next Return PersistableIdleAction.Unload End Function
wfApp.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e) { // Send the current WriteLine outputs to the status window. var writers = e.GetInstanceExtensions<StringWriter>(); foreach (var writer in writers) { UpdateStatus(writer.ToString()); } return PersistableIdleAction.Unload; };
PersistableIdleAction 列舉有三個值:None、Persist 及 Unload。 Persist 會使工作流程繼續持續,但不會導致工作流程卸載。 Unload 會使工作流程繼續持續並卸載。
下列範例是完成的
ConfigureWorkflowApplication
方法。Private Sub ConfigureWorkflowApplication(wfApp As WorkflowApplication) ' Configure the persistence store. wfApp.InstanceStore = store ' Add a StringWriter to the extensions. This captures the output ' from the WriteLine activities so we can display it in the form. Dim sw As New StringWriter() wfApp.Extensions.Add(sw) wfApp.Completed = _ Sub(e As WorkflowApplicationCompletedEventArgs) If e.CompletionState = ActivityInstanceState.Faulted Then UpdateStatus($"Workflow Terminated. Exception: {e.TerminationException.GetType().FullName}{vbCrLf}{e.TerminationException.Message}") ElseIf e.CompletionState = ActivityInstanceState.Canceled Then UpdateStatus("Workflow Canceled.") Else Dim turns As Integer = Convert.ToInt32(e.Outputs("Turns")) UpdateStatus($"Congratulations, you guessed the number in {turns} turns.") End If GameOver() End Sub wfApp.Aborted = _ Sub(e As WorkflowApplicationAbortedEventArgs) UpdateStatus($"Workflow Aborted. Exception: {e.Reason.GetType().FullName}{vbCrLf}{e.Reason.Message}") End Sub wfApp.OnUnhandledException = _ Function(e As WorkflowApplicationUnhandledExceptionEventArgs) UpdateStatus($"Unhandled Exception: {e.UnhandledException.GetType().FullName}{vbCrLf}{e.UnhandledException.Message}") GameOver() Return UnhandledExceptionAction.Terminate End Function wfApp.PersistableIdle = _ Function(e As WorkflowApplicationIdleEventArgs) ' Send the current WriteLine outputs to the status window. Dim writers = e.GetInstanceExtensions(Of StringWriter)() For Each writer In writers UpdateStatus(writer.ToString()) Next Return PersistableIdleAction.Unload End Function End Sub
private void ConfigureWorkflowApplication(WorkflowApplication wfApp) { // Configure the persistence store. wfApp.InstanceStore = store; // Add a StringWriter to the extensions. This captures the output // from the WriteLine activities so we can display it in the form. var sw = new StringWriter(); wfApp.Extensions.Add(sw); wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e) { if (e.CompletionState == ActivityInstanceState.Faulted) { UpdateStatus($"Workflow Terminated. Exception: {e.TerminationException.GetType().FullName}\r\n{e.TerminationException.Message}"); } else if (e.CompletionState == ActivityInstanceState.Canceled) { UpdateStatus("Workflow Canceled."); } else { int turns = Convert.ToInt32(e.Outputs["Turns"]); UpdateStatus($"Congratulations, you guessed the number in {turns} turns."); } GameOver(); }; wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e) { UpdateStatus($"Workflow Aborted. Exception: {e.Reason.GetType().FullName}\r\n{e.Reason.Message}"); }; wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e) { UpdateStatus($"Unhandled Exception: {e.UnhandledException.GetType().FullName}\r\n{e.UnhandledException.Message}"); GameOver(); return UnhandledExceptionAction.Terminate; }; wfApp.PersistableIdle = delegate(WorkflowApplicationIdleEventArgs e) { // Send the current WriteLine outputs to the status window. var writers = e.GetInstanceExtensions<StringWriter>(); foreach (var writer in writers) { UpdateStatus(writer.ToString()); } return PersistableIdleAction.Unload; }; }
使其能夠啟動和繼續使用多個工作流程類型
主機必須提供工作流程定義,才能繼續工作流程執行個體。 本教學課程包含三種工作流程型別,後續的教學課程將介紹這些類型的多個版本。 WorkflowIdentity
提供方法,讓主應用程式能夠將識別資訊與持續的工作流程執行個體建立關聯。 本節中的步驟示範如何建立公用程式類別,以協助將持續性工作流程執行個體的工作流程識別對應至相對應的工作流程定義。 如需 WorkflowIdentity
和版本控制的詳細資訊,請參閱使用 WorkflowIdentity 和版本控制。
在方案總管中,以滑鼠右鍵按一下 NumberGuessWorkflowHost,然後選擇 [新增]、[類別]。 在 [名稱] 方塊中鍵入
WorkflowVersionMap
,然後按一下 [新增]。將下列
using
或Imports
陳述式加入至檔案最上方的其他using
或Imports
陳述式。Imports System.Activities Imports NumberGuessWorkflowActivities
using System.Activities; using NumberGuessWorkflowActivities;
用下列宣告取代
WorkflowVersionMap
類別宣告。Public Module WorkflowVersionMap Dim map As Dictionary(Of WorkflowIdentity, Activity) ' Current version identities. Public StateMachineNumberGuessIdentity As WorkflowIdentity Public FlowchartNumberGuessIdentity As WorkflowIdentity Public SequentialNumberGuessIdentity As WorkflowIdentity Sub New() map = New Dictionary(Of WorkflowIdentity, Activity) ' Add the current workflow version identities. StateMachineNumberGuessIdentity = New WorkflowIdentity With { .Name = "StateMachineNumberGuessWorkflow", .Version = New Version(1, 0, 0, 0) } FlowchartNumberGuessIdentity = New WorkflowIdentity With { .Name = "FlowchartNumberGuessWorkflow", .Version = New Version(1, 0, 0, 0) } SequentialNumberGuessIdentity = New WorkflowIdentity With { .Name = "SequentialNumberGuessWorkflow", .Version = New Version(1, 0, 0, 0) } map.Add(StateMachineNumberGuessIdentity, New StateMachineNumberGuessWorkflow()) map.Add(FlowchartNumberGuessIdentity, New FlowchartNumberGuessWorkflow()) map.Add(SequentialNumberGuessIdentity, New SequentialNumberGuessWorkflow()) End Sub Public Function GetWorkflowDefinition(identity As WorkflowIdentity) As Activity Return map(identity) End Function Public Function GetIdentityDescription(identity As WorkflowIdentity) As String Return identity.ToString() End Function End Module
public static class WorkflowVersionMap { static Dictionary<WorkflowIdentity, Activity> map; // Current version identities. static public WorkflowIdentity StateMachineNumberGuessIdentity; static public WorkflowIdentity FlowchartNumberGuessIdentity; static public WorkflowIdentity SequentialNumberGuessIdentity; static WorkflowVersionMap() { map = new Dictionary<WorkflowIdentity, Activity>(); // Add the current workflow version identities. StateMachineNumberGuessIdentity = new WorkflowIdentity { Name = "StateMachineNumberGuessWorkflow", Version = new Version(1, 0, 0, 0) }; FlowchartNumberGuessIdentity = new WorkflowIdentity { Name = "FlowchartNumberGuessWorkflow", Version = new Version(1, 0, 0, 0) }; SequentialNumberGuessIdentity = new WorkflowIdentity { Name = "SequentialNumberGuessWorkflow", Version = new Version(1, 0, 0, 0) }; map.Add(StateMachineNumberGuessIdentity, new StateMachineNumberGuessWorkflow()); map.Add(FlowchartNumberGuessIdentity, new FlowchartNumberGuessWorkflow()); map.Add(SequentialNumberGuessIdentity, new SequentialNumberGuessWorkflow()); } public static Activity GetWorkflowDefinition(WorkflowIdentity identity) { return map[identity]; } public static string GetIdentityDescription(WorkflowIdentity identity) { return identity.ToString(); } }
WorkflowVersionMap
包含三個工作流程識別,其對應於此教學課程中的三個工作流程定義,在下列章節中,啟動及繼續使用工作流程時會使用這些識別。
啟動新的工作流程
加入
Click
的NewGame
處理常式。 若要新增處理常式,請切換至表單的 [設計檢視],然後按兩下NewGame
。 會加入NewGame_Click
處理常式,且表單的檢視會切換成程式碼檢視。 每當使用者按一下此按鈕,就會啟動新的工作流程。Private Sub NewGame_Click(sender As Object, e As EventArgs) Handles NewGame.Click End Sub
private void NewGame_Click(object sender, EventArgs e) { }
將下列程式碼加入至 Click 處理常式。 此程式碼會建立工作流程的輸入引數字典,以引數名稱為索引鍵。 此字典有一個項目,其中包含從範圍下拉式方塊擷取之隨機產生號碼的範圍。
Dim inputs As New Dictionary(Of String, Object)() inputs.Add("MaxNumber", Convert.ToInt32(NumberRange.SelectedItem))
var inputs = new Dictionary<string, object>(); inputs.Add("MaxNumber", Convert.ToInt32(NumberRange.SelectedItem));
接下來,加入下列啟動工作流程的程式碼。 會使用
WorkflowIdentity
Helper 類別,擷取對應至所選工作流程型別的WorkflowVersionMap
和工作流程定義。 接下來會使用工作流程定義WorkflowApplication
和輸入引數的字典來建立新的WorkflowIdentity
執行個體。Dim identity As WorkflowIdentity = Nothing Select Case WorkflowType.SelectedItem.ToString() Case "SequentialNumberGuessWorkflow" identity = WorkflowVersionMap.SequentialNumberGuessIdentity Case "StateMachineNumberGuessWorkflow" identity = WorkflowVersionMap.StateMachineNumberGuessIdentity Case "FlowchartNumberGuessWorkflow" identity = WorkflowVersionMap.FlowchartNumberGuessIdentity End Select Dim wf As Activity = WorkflowVersionMap.GetWorkflowDefinition(identity) Dim wfApp = New WorkflowApplication(wf, inputs, identity)
WorkflowIdentity identity = null; switch (WorkflowType.SelectedItem.ToString()) { case "SequentialNumberGuessWorkflow": identity = WorkflowVersionMap.SequentialNumberGuessIdentity; break; case "StateMachineNumberGuessWorkflow": identity = WorkflowVersionMap.StateMachineNumberGuessIdentity; break; case "FlowchartNumberGuessWorkflow": identity = WorkflowVersionMap.FlowchartNumberGuessIdentity; break; }; Activity wf = WorkflowVersionMap.GetWorkflowDefinition(identity); WorkflowApplication wfApp = new WorkflowApplication(wf, inputs, identity);
接下來,加入下列程式碼,此程式碼會將工作流程加入到工作流程清單,並在表單上顯示該工作流程的版本資訊。
' Add the workflow to the list and display the version information. workflowStarting = True InstanceId.SelectedIndex = InstanceId.Items.Add(wfApp.Id) WorkflowVersion.Text = identity.ToString() workflowStarting = False
// Add the workflow to the list and display the version information. workflowStarting = true; InstanceId.SelectedIndex = InstanceId.Items.Add(wfApp.Id); WorkflowVersion.Text = identity.ToString(); workflowStarting = false;
呼叫
ConfigureWorkflowApplication
以設定執行個體存放區、擴充,以及此WorkflowApplication
執行個體的工作流程開發週期處理常式。' Configure the instance store, extensions, and ' workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp)
// Configure the instance store, extensions, and // workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp);
最後,請呼叫
Run
。' Start the workflow. wfApp.Run()
// Start the workflow. wfApp.Run();
下列範例是已完成的
NewGame_Click
處理常式。Private Sub NewGame_Click(sender As Object, e As EventArgs) Handles NewGame.Click ' Start a new workflow. Dim inputs As New Dictionary(Of String, Object)() inputs.Add("MaxNumber", Convert.ToInt32(NumberRange.SelectedItem)) Dim identity As WorkflowIdentity = Nothing Select Case WorkflowType.SelectedItem.ToString() Case "SequentialNumberGuessWorkflow" identity = WorkflowVersionMap.SequentialNumberGuessIdentity Case "StateMachineNumberGuessWorkflow" identity = WorkflowVersionMap.StateMachineNumberGuessIdentity Case "FlowchartNumberGuessWorkflow" identity = WorkflowVersionMap.FlowchartNumberGuessIdentity End Select Dim wf As Activity = WorkflowVersionMap.GetWorkflowDefinition(identity) Dim wfApp = New WorkflowApplication(wf, inputs, identity) ' Add the workflow to the list and display the version information. workflowStarting = True InstanceId.SelectedIndex = InstanceId.Items.Add(wfApp.Id) WorkflowVersion.Text = identity.ToString() workflowStarting = False ' Configure the instance store, extensions, and ' workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp) ' Start the workflow. wfApp.Run() End Sub
private void NewGame_Click(object sender, EventArgs e) { var inputs = new Dictionary<string, object>(); inputs.Add("MaxNumber", Convert.ToInt32(NumberRange.SelectedItem)); WorkflowIdentity identity = null; switch (WorkflowType.SelectedItem.ToString()) { case "SequentialNumberGuessWorkflow": identity = WorkflowVersionMap.SequentialNumberGuessIdentity; break; case "StateMachineNumberGuessWorkflow": identity = WorkflowVersionMap.StateMachineNumberGuessIdentity; break; case "FlowchartNumberGuessWorkflow": identity = WorkflowVersionMap.FlowchartNumberGuessIdentity; break; }; Activity wf = WorkflowVersionMap.GetWorkflowDefinition(identity); var wfApp = new WorkflowApplication(wf, inputs, identity); // Add the workflow to the list and display the version information. workflowStarting = true; InstanceId.SelectedIndex = InstanceId.Items.Add(wfApp.Id); WorkflowVersion.Text = identity.ToString(); workflowStarting = false; // Configure the instance store, extensions, and // workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp); // Start the workflow. wfApp.Run(); }
繼續使用工作流程
加入
Click
的EnterGuess
處理常式。 若要新增處理常式,請切換至表單的 [設計檢視],然後按兩下EnterGuess
。 每當使用者按一下此按鈕,就會繼續使用該工作流程。Private Sub EnterGuess_Click(sender As Object, e As EventArgs) Handles EnterGuess.Click End Sub
private void EnterGuess_Click(object sender, EventArgs e) { }
加入下列程式碼,以確保已在工作流程清單中選取工作流程,且使用者的猜測是有效的。
If WorkflowInstanceId = Guid.Empty Then MessageBox.Show("Please select a workflow.") Return End If Dim userGuess As Integer If Not Int32.TryParse(Guess.Text, userGuess) Then MessageBox.Show("Please enter an integer.") Guess.SelectAll() Guess.Focus() Return End If
if (WorkflowInstanceId == Guid.Empty) { MessageBox.Show("Please select a workflow."); return; } int guess; if (!Int32.TryParse(Guess.Text, out guess)) { MessageBox.Show("Please enter an integer."); Guess.SelectAll(); Guess.Focus(); return; }
接下來,擷取持續性工作流程執行個體的
WorkflowApplicationInstance
。WorkflowApplicationInstance
代表尚未與工作流程定義相關聯的持續性工作流程執行個體。DefinitionIdentity
的WorkflowApplicationInstance
包含持續性工作流程執行個體的WorkflowIdentity
。 在本教學課程中,會使用WorkflowVersionMap
公用程式類別,將WorkflowIdentity
對應至正確的工作流程定義。 擷取工作流程定義後,會使用正確的工作流程定義來建立WorkflowApplication
。Dim instance As WorkflowApplicationInstance = _ WorkflowApplication.GetInstance(WorkflowInstanceId, store) ' Use the persisted WorkflowIdentity to retrieve the correct workflow ' definition from the dictionary. Dim wf As Activity = _ WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity) ' Associate the WorkflowApplication with the correct definition Dim wfApp As New WorkflowApplication(wf, instance.DefinitionIdentity)
WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(WorkflowInstanceId, store); // Use the persisted WorkflowIdentity to retrieve the correct workflow // definition from the dictionary. Activity wf = WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity); // Associate the WorkflowApplication with the correct definition var wfApp = new WorkflowApplication(wf, instance.DefinitionIdentity);
建立
WorkflowApplication
後,呼叫ConfigureWorkflowApplication
,以設定執行個體存放區、工作流程開發週期處理常式和擴充。 每次建立新的WorkflowApplication
時,都必須完成這些步驟,而且必須在將工作流程執行個體載入到WorkflowApplication
之前完成。 載入工作流程後,會繼續進行使用者的猜測。' Configure the extensions and lifecycle handlers. ' Do this before the instance is loaded. Once the instance is ' loaded it is too late to add extensions. ConfigureWorkflowApplication(wfApp) ' Load the workflow. wfApp.Load(instance) ' Resume the workflow. wfApp.ResumeBookmark("EnterGuess", userGuess)
// Configure the extensions and lifecycle handlers. // Do this before the instance is loaded. Once the instance is // loaded it is too late to add extensions. ConfigureWorkflowApplication(wfApp); // Load the workflow. wfApp.Load(instance); // Resume the workflow. wfApp.ResumeBookmark("EnterGuess", guess);
最後,清除猜測文字方塊,並準備表單以接受另一種猜測。
' Clear the Guess textbox. Guess.Clear() Guess.Focus()
// Clear the Guess textbox. Guess.Clear(); Guess.Focus();
下列範例是已完成的
EnterGuess_Click
處理常式。Private Sub EnterGuess_Click(sender As Object, e As EventArgs) Handles EnterGuess.Click If WorkflowInstanceId = Guid.Empty Then MessageBox.Show("Please select a workflow.") Return End If Dim userGuess As Integer If Not Int32.TryParse(Guess.Text, userGuess) Then MessageBox.Show("Please enter an integer.") Guess.SelectAll() Guess.Focus() Return End If Dim instance As WorkflowApplicationInstance = _ WorkflowApplication.GetInstance(WorkflowInstanceId, store) ' Use the persisted WorkflowIdentity to retrieve the correct workflow ' definition from the dictionary. Dim wf As Activity = _ WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity) ' Associate the WorkflowApplication with the correct definition Dim wfApp As New WorkflowApplication(wf, instance.DefinitionIdentity) ' Configure the extensions and lifecycle handlers. ' Do this before the instance is loaded. Once the instance is ' loaded it is too late to add extensions. ConfigureWorkflowApplication(wfApp) ' Load the workflow. wfApp.Load(instance) ' Resume the workflow. wfApp.ResumeBookmark("EnterGuess", userGuess) ' Clear the Guess textbox. Guess.Clear() Guess.Focus() End Sub
private void EnterGuess_Click(object sender, EventArgs e) { if (WorkflowInstanceId == Guid.Empty) { MessageBox.Show("Please select a workflow."); return; } int guess; if (!Int32.TryParse(Guess.Text, out guess)) { MessageBox.Show("Please enter an integer."); Guess.SelectAll(); Guess.Focus(); return; } WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(WorkflowInstanceId, store); // Use the persisted WorkflowIdentity to retrieve the correct workflow // definition from the dictionary. Activity wf = WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity); // Associate the WorkflowApplication with the correct definition var wfApp = new WorkflowApplication(wf, instance.DefinitionIdentity); // Configure the extensions and lifecycle handlers. // Do this before the instance is loaded. Once the instance is // loaded it is too late to add extensions. ConfigureWorkflowApplication(wfApp); // Load the workflow. wfApp.Load(instance); // Resume the workflow. wfApp.ResumeBookmark("EnterGuess", guess); // Clear the Guess textbox. Guess.Clear(); Guess.Focus(); }
終止工作流程
加入
Click
的QuitGame
處理常式。 若要新增處理常式,請切換至表單的 [設計檢視],然後按兩下QuitGame
。 每當使用者按一下此按鈕,就會終止目前選取的工作流程。Private Sub QuitGame_Click(sender As Object, e As EventArgs) Handles QuitGame.Click End Sub
private void QuitGame_Click(object sender, EventArgs e) { }
將下列程式碼加入至
QuitGame_Click
處理常式。 此程式碼會先檢查,確定已在工作流程清單中選取工作流程。 接著會將持續性執行個體載入到WorkflowApplicationInstance
、使用DefinitionIdentity
來判斷正確的工作流程定義,然後初始化WorkflowApplication
。 接下來會呼叫ConfigureWorkflowApplication
以設定擴充和工作流程開發週期處理常式。 設定WorkflowApplication
之後,會載入它,然後呼叫Terminate
。If WorkflowInstanceId = Guid.Empty Then MessageBox.Show("Please select a workflow.") Return End If Dim instance As WorkflowApplicationInstance = _ WorkflowApplication.GetInstance(WorkflowInstanceId, store) ' Use the persisted WorkflowIdentity to retrieve the correct workflow ' definition from the dictionary. Dim wf As Activity = WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity) ' Associate the WorkflowApplication with the correct definition. Dim wfApp As New WorkflowApplication(wf, instance.DefinitionIdentity) ' Configure the extensions and lifecycle handlers. ConfigureWorkflowApplication(wfApp) ' Load the workflow. wfApp.Load(instance) ' Terminate the workflow. wfApp.Terminate("User resigns.")
if (WorkflowInstanceId == Guid.Empty) { MessageBox.Show("Please select a workflow."); return; } WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(WorkflowInstanceId, store); // Use the persisted WorkflowIdentity to retrieve the correct workflow // definition from the dictionary. Activity wf = WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity); // Associate the WorkflowApplication with the correct definition var wfApp = new WorkflowApplication(wf, instance.DefinitionIdentity); // Configure the extensions and lifecycle handlers ConfigureWorkflowApplication(wfApp); // Load the workflow. wfApp.Load(instance); // Terminate the workflow. wfApp.Terminate("User resigns.");
若要建置並執行應用程式
在方案總管中按兩下 Program.cs (或 Module1.vb),以顯示程式碼。
將下列
using
(或Imports
) 陳述式加入至檔案最上方的其他using
(或Imports
) 陳述式。Imports System.Windows.Forms
using System.Windows.Forms;
透過操作說明:執行工作流程移除或取消註解現有的工作流程裝載程式碼,並將其取代為下列程式碼。
Sub Main() Application.EnableVisualStyles() Application.Run(New WorkflowHostForm()) End Sub
static void Main(string[] args) { Application.EnableVisualStyles(); Application.Run(new WorkflowHostForm()); }
在方案總管中,以滑鼠右鍵按一下 NumberGuessWorkflowHost,然後選擇 [屬性]。 在 [應用程式] 索引標籤中,將 [輸出型別] 指定為 [Windows 應用程式]。 此步驟是選用性的,但如果不進行此步驟,除了表單外還會顯示主控台視窗。
按 Ctrl+Shift+B 建置應用程式。
確定已將 NumberGuessWorkflowHost 設定為啟動應用程式,然後按 Ctrl+F5 以啟動應用程式。
請選取猜謎遊戲的範圍和要啟動的工作流程型別,然後按一下 [新遊戲]。 在 [猜謎] 方塊中輸入猜測,然後按一下 [開始] 提交猜測。 請注意,
WriteLine
活動的輸出會顯示在表單上。啟動數個使用不同工作流程型別和數字範圍的工作流程、輸入猜測,並且從 [工作流程執行個體識別碼] 清單中選取以切換工作流程。
請注意,當您切換到新的工作流程時,先前的猜測和工作流程的進度都不會顯示在狀態視窗中。 不顯示狀態的原因是未擷取狀態,也未儲存在任何位置。 在本教學課程的下一個步驟操作說明:建立自訂追蹤參與者中,您會建立儲存此資訊的自訂追蹤參與者。