Windows Workflow Foundation (WF) の中心的な機能の 1 つは、アイドル状態のワークフローを永続化し、データベースにアンロードするランタイムの機能です。 「方法: ワークフローを実行する」の手順では、コンソール アプリケーションを使用したワークフロー ホスティングの基本を示しました。 ワークフローの開始、ワークフロー ライフサイクル ハンドラー、およびブックマークの再開の例を紹介しました。 ワークフローの永続化を効果的に説明するためには、複数のワークフロー インスタンスの開始と再開をサポートするより複雑なワークフロー ホストが必要です。 チュートリアルのこの手順では、複数のワークフロー インスタンスの開始と再開およびワークフローの永続化をサポートする Windows フォーム ホスト アプリケーションを作成する方法について説明します。また、この手順は、以降の手順で説明する追跡やバージョン管理などの高度な機能の基礎となります。
注意
このチュートリアルの手順と以降の手順では、「方法: ワークフローを作成する」の 3 つのワークフローの種類すべてを使用します。
永続性データベースを作成するには
SQL Server Management Studio を開き、 .\SQLEXPRESS などのローカル サーバーに接続します。 ローカル サーバーの [Databases] ノードを右クリックし、 [新しいデータベース] をクリックします。 新しいデータベースに WF45GettingStartedTutorial という名前を付け、その他の値はすべてそのままにし、 [OK] をクリックします。
注意
データベースを作成する前に、ローカル サーバーに対する Create Database 権限があることを確認してください。
[ファイル] メニューの [開く] をポイントし、 [ファイル] をクリックします。 次のフォルダーに移動します: C:\Windows\Microsoft.NET\Framework\v4.0.30319\sql\en
次の 2 つのファイルを選択し、 [開く] をクリックします。
SqlWorkflowInstanceStoreLogic.sql
SqlWorkflowInstanceStoreSchema.sql
[ウィンドウ] メニューから SqlWorkflowInstanceStoreSchema.sql を選択します。 WF45GettingStartedTutorial が [利用可能なデータベース] ボックスの一覧で選択されていることを確認し、 [クエリ] メニューの [実行] を選択します。
[ウィンドウ] メニューから SqlWorkflowInstanceStoreLogic.sql を選択します。 WF45GettingStartedTutorial が [利用可能なデータベース] ボックスの一覧で選択されていることを確認し、 [クエリ] メニューの [実行] を選択します。
警告
前の 2 つの手順を正しい順序で実行することが重要です。 クエリが正しい順序で実行されないと、エラーが発生し、永続性データベースは正しく構成されません。
DurableInstancing アセンブリへの参照を追加するには
ソリューション エクスプローラーで NumberGuessWorkflowHost を右クリックし、 [参照の追加] をクリックします。
[参照の追加] ボックスの一覧の [アセンブリ] を選択し、[アセンブリの検索] ボックスに「
DurableInstancing」と入力します。 これにより、アセンブリがフィルター処理され、目的の参照を簡単に選択できます。[検索結果] の一覧から System.Activities.DurableInstancing と System.Runtime.DurableInstancing の横にあるチェック ボックスをオンにし、 [OK] をクリックします。
ワークフロー ホスト フォームを作成するには
ソリューション エクスプローラーで NumberGuessWorkflowHost を右クリックし、 [追加] をポイントして、 [新しい項目] をクリックします。
[インストール済み] テンプレートの一覧で、[Windows フォーム] を選択し、[名前] ボックスに「
WorkflowHostForm」と入力して、[追加] をクリックします。フォームの次のプロパティを構成します。
プロパティ [値] FormBorderStyle FixedSingle MaximizeBox × サイズ 400, 420 次のコントロールを指定された順序でフォームに追加し、指示に従ってプロパティを構成します。
コントロール プロパティ: 値 Button Name: NewGame
Location: 13, 13
Size: 75, 23
Text: New GameLabel Location: 94, 18
Text: Guess a number from 1 toComboBox Name: NumberRange
DropDownStyle: DropDownList
Items: 10, 100, 1000
Location: 228, 12
Size: 143, 21Label Location: 13, 43
Text: Workflow typeComboBox Name: WorkflowType
DropDownStyle: DropDownList
Items: StateMachineNumberGuessWorkflow, FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflow
Location: 94, 40
Size: 277, 21Label Name: WorkflowVersion
Location: 13, 362
Text: Workflow versionGroupBox Location: 13, 67
Size: 358, 287
Text: Game注意
次のコントロールを追加するときに、それらを GroupBox に配置します。
コントロール プロパティ: 値 Label Location: 7, 20
Text: Workflow Instance IdComboBox Name: InstanceId
DropDownStyle: DropDownList
Location: 121, 17
Size: 227, 21Label Location: 7, 47
Text: GuessTextBox Name: Guess
Location: 50, 44
Size: 65, 20Button Name: EnterGuess
Location: 121, 42
Size: 75, 23
Text: Enter GuessButton Name: QuitGame
Location: 274, 42
Size: 75, 23
Text: QuitTextBox Name: WorkflowStatus
Location: 10, 73
Multiline: True
ReadOnly: True
ScrollBars: Vertical
Size: 338, 208フォームの AcceptButton プロパティを EnterGuess に設定します。
次の例は完成したフォームを示しています。

フォームのプロパティとヘルパー メソッドを追加するには
このセクションの手順では、フォーム クラスに、数値推測ワークフローの実行と再開をサポートするようフォームの UI を構成するプロパティとヘルパー メソッドを追加します。
ソリューション エクスプローラーで WorkflowHostForm を右クリックし、 [コードの表示] をクリックします。
次の
using(またはImports) ステートメントを、他のusing(またはImports) ステートメントを含むファイルの先頭に追加します。Imports System.Activities Imports System.Activities.DurableInstancing Imports System.Data.SqlClient Imports System.IO Imports System.Windows.Formsusing System.Activities; using System.Activities.DurableInstancing; using System.Data.SqlClient; using System.IO; using System.Windows.Forms;WorkflowHostForm クラスに次のメンバー宣言を追加します。
重要
Microsoft では、使用可能な最も安全な認証フローを使用することをお勧めします。 Azure SQL に接続する場合は、Azure リソースの管理 ID が推奨される認証方法です。
Const connectionString = "Server=.\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI" Dim store As SqlWorkflowInstanceStore Dim workflowStarting As Booleanconst string connectionString = "Server=.\\SQLEXPRESS;Initial Catalog=WF45GettingStartedTutorial;Integrated Security=SSPI"; SqlWorkflowInstanceStore store; bool workflowStarting;Note
接続文字列が異なる場合は、使用しているデータベースを参照するように
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 Propertypublic Guid WorkflowInstanceId { get { return InstanceId.SelectedIndex == -1 ? Guid.Empty : (Guid)InstanceId.SelectedItem; } }InstanceIdコンボ ボックスには、永続化されたワークフロー インスタンス ID の一覧が表示され、WorkflowInstanceIdプロパティは現在選択されているワークフローを返します。フォームの
Loadイベントのハンドラーを追加します。 ハンドラーを追加するには、フォームのデザイン ビューに切り替え、 [プロパティ] ウィンドウの上部にある [イベント] アイコンをクリックして、 [Load] をダブルクリックします。Private Sub WorkflowHostForm_Load(sender As Object, e As EventArgs) Handles Me.Load End Subprivate 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 Subprivate 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 Ifif (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 Subusing (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コンボ ボックスにインスタンス ID を追加します。次の
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 Subprivate 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 コンボ ボックスから完了したワークフローのインスタンス ID を削除して、フォームの 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 Subprivate 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 Subprivate 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 SubwfApp.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 FunctionwfApp.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 FunctionwfApp.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 の 3 つの値があります。 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 Subprivate 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; }; }
複数のワークフローの種類を開始および再開できるようにするには
ワークフロー インスタンスを再開するには、ホストはワークフロー定義を指定する必要があります。 このチュートリアルには 3 種類のワークフローがあり、以降の手順では、これらの種類の複数のバージョンを指定します。 WorkflowIdentity を使用すると、ホスト アプリケーションは、識別情報を永続化されたワークフロー インスタンスに関連付けることができます。 このセクションの手順では、永続化されたワークフロー インスタンスから対応するワークフロー定義へのワークフロー ID のマッピングに役立つユーティリティ クラスの作成方法を示します。 WorkflowIdentity とバージョン管理の詳細については、「WorkflowIdentity と Versioning の使用」を参照してください。
ソリューション エクスプローラーで NumberGuessWorkflowHost を右クリックし、 [追加] 、 [クラス] の順に選択します。 [名前] ボックスに「
WorkflowVersionMap」と入力し、[追加] をクリックします。次の
usingまたはImportsステートメントを、他のusingまたはImportsステートメントを含むファイルの先頭に追加します。Imports System.Activities Imports NumberGuessWorkflowActivitiesusing 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 Modulepublic 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は、このチュートリアルの 3 つのワークフロー定義にマップされる 3 つのワークフロー ID を格納しており、以降のセクションでワークフローが開始および再開されるときに使用されます。
新しいワークフローを開始するには
ClickのNewGameハンドラーを追加します。 ハンドラーを追加するには、フォームのデザイン ビューに切り替え、NewGameをダブルクリックします。NewGame_Clickハンドラーが追加され、ビューがフォームのコード ビューに切り替わります。 ユーザーがこのボタンをクリックするたびに、新しいワークフローが開始されます。Private Sub NewGame_Click(sender As Object, e As EventArgs) Handles NewGame.Click End Subprivate void NewGame_Click(object sender, EventArgs e) { }Click ハンドラーに次のコードを追加します。 このコードにより、引数名によってキー指定された、ワークフローの入力引数のディクショナリが作成されます。 このディクショナリには、範囲のコンボ ボックスから取得したランダムに生成された数値の範囲を含む 1 つのエントリがあります。
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とワークフロー定義が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 Subprivate 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 Subprivate 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 Ifif (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);最後に、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 Subprivate 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 Subprivate 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.Formsusing System.Windows.Forms;「方法: ワークフローを実行する」の既存のワークフロー ホスティング コードを削除またはコメント アウトし、次のコードに置き換えます。
Sub Main() Application.EnableVisualStyles() Application.Run(New WorkflowHostForm()) End Substatic void Main(string[] args) { Application.EnableVisualStyles(); Application.Run(new WorkflowHostForm()); }ソリューション エクスプローラーで NumberGuessWorkflowHost を右クリックし、 [プロパティ] をクリックします。 [アプリケーション] タブで、 [出力の種類] に [Windows アプリケーション] を指定します。 この手順は省略可能ですが、省略した場合は、フォームに加えてコンソール ウィンドウが表示されます。
Ctrl キーと Shift キーを押しながら B キーを押してアプリケーションをビルドします。
NumberGuessWorkflowHost がスタートアップ アプリケーションとして設定されていることを確認し、Ctrl キーを押しながら F5 キーを押してアプリケーションを起動します。
推測ゲームの範囲と開始するワークフローの種類を選択し、 [New Game] をクリックします。 [Guess] ボックスに推定値を入力し、 [Go] をクリックして推定値を送信します。
WriteLineアクティビティからの出力がフォームに表示されることに注意してください。異なるワークフローの種類と数値の範囲を使用して複数のワークフローを開始し、推定値をいくつか入力します。ワークフローを切り替えるには、 [Workflow Instance Id] ボックスの一覧から選択します。
新しいワークフローに切り替えると、前の推定値とワークフローの進行状況はステータス ウィンドウに表示されません。 ステータスが利用できない理由は、ステータスがキャプチャされず、どこにも保存されないためです。 チュートリアルの次の手順「方法: カスタム追跡参加要素を作成する」では、この情報を保存するカスタム追跡参加要素を作成します。
.NET