方法: カスタム追跡参加要素を作成する

ワークフロー追跡により、ワークフロー実行の状態が視覚的に示されます。 ワークフロー ランタイムによって、ワークフローのライフサイクル イベント、アクティビティのライフサイクル イベント、ブックマークの再開、およびエラーについて説明する追跡レコードが出力されます。 これらの追跡レコードは、追跡参加要素によって使用されます。 Windows Workflow Foundation (WF) には、追跡レコードを Event Tracing for Windows (ETW) イベントとして書き込む標準の追跡参加要素が含まれています。 これで要件が満たされない場合は、カスタムの追跡参加要素を作成することもできます。 チュートリアルのこの手順では、WriteLine アクティビティの出力をキャプチャするカスタム追跡参加要素と追跡プロファイルを作成して、ユーザーに表示できるようにする方法について説明します。

カスタム追跡参加要素を作成するには

  1. ソリューション エクスプローラーNumberGuessWorkflowHost を右クリックし、 [追加] をポイントして、 [クラス] をクリックします。 StatusTrackingParticipant[名前]ボックスに「」と入力して、[追加] をクリックします。

  2. 次の using (または Imports) ステートメントを、他の using (または Imports) ステートメントを含むファイルの先頭に追加します。

    Imports System.Activities.Tracking  
    Imports System.IO  
    
    using System.Activities.Tracking;  
    using System.IO;  
    
  3. StatusTrackingParticipant クラスを変更して、TrackingParticipant から継承されるようにします。

    Public Class StatusTrackingParticipant  
        Inherits TrackingParticipant  
    
    End Class  
    
    class StatusTrackingParticipant : TrackingParticipant  
    {  
    }  
    
  4. 次の Track メソッド オーバーライドを追加します。 追跡レコードには、いくつかの種類があります。 ここでは、アクティビティ追跡レコードに含まれる WriteLine アクティビティの出力に注目します。 TrackingRecordActivityTrackingRecord アクティビティの WriteLine である場合は、ワークフローの Text に基づいた名前の付いたファイルに WriteLineInstanceId が追加されます。 このチュートリアルでは、ファイルはホスト アプリケーションの現在のフォルダーに保存されます。

    Protected Overrides Sub Track(record As TrackingRecord, timeout As TimeSpan)  
        Dim asr As ActivityStateRecord = TryCast(record, ActivityStateRecord)  
    
        If Not asr Is Nothing Then  
            If asr.State = ActivityStates.Executing And _  
            asr.Activity.TypeName = "System.Activities.Statements.WriteLine" Then  
    
                'Append the WriteLine output to the tracking  
                'file for this instance.  
                Using writer As StreamWriter = File.AppendText(record.InstanceId.ToString())  
                    writer.WriteLine(asr.Arguments("Text"))  
                    writer.Close()  
                End Using  
            End If  
        End If  
    End Sub  
    
    protected override void Track(TrackingRecord record, TimeSpan timeout)  
    {  
        ActivityStateRecord asr = record as ActivityStateRecord;  
    
        if (asr != null)  
        {  
            if (asr.State == ActivityStates.Executing &&  
                asr.Activity.TypeName == "System.Activities.Statements.WriteLine")  
            {  
                // Append the WriteLine output to the tracking  
                // file for this instance  
                using (StreamWriter writer = File.AppendText(record.InstanceId.ToString()))  
                {  
                    writer.WriteLine(asr.Arguments["Text"]);  
                    writer.Close();  
                }  
            }  
        }  
    }  
    

    追跡プロファイルを指定しない場合は、既定の追跡プロファイルが使用されます。 既定の追跡プロファイルを使用する場合は、すべての ActivityStates に関する追跡レコードが出力されます。 ここでは、WriteLine アクティビティのライフサイクル中に 1 回だけテキストをキャプチャする必要があるため、ActivityStates.Executing 状態からテキストを抽出するだけです。 「追跡プロファイルを作成して追跡参加要素を登録するには」では、WriteLine ActivityStates.Executing の追跡レコードのみが出力されるように指定する追跡プロファイルを作成します。

追跡プロファイルを作成して追跡参加要素を登録するには

  1. ソリューション エクスプローラーWorkflowHostForm を右クリックし、 [コードの表示] をクリックします。

  2. 次の using (または Imports) ステートメントを、他の using (または Imports) ステートメントを含むファイルの先頭に追加します。

    Imports System.Activities.Tracking  
    
    using System.Activities.Tracking;  
    
  3. ワークフロー拡張機能に ConfigureWorkflowApplication を追加するコードの直後で、ワークフロー ライフサイクル ハンドラーの前に、StringWriter に次のコードを追加します。

    'Add the custom tracking participant with a tracking profile  
    'that only emits tracking records for WriteLine activities.  
    Dim query As New ActivityStateQuery()  
    query.ActivityName = "WriteLine"  
    query.States.Add(ActivityStates.Executing)  
    query.Arguments.Add("Text")  
    
    Dim profile As New TrackingProfile()  
    profile.Queries.Add(query)  
    
    Dim stp As New StatusTrackingParticipant()  
    stp.TrackingProfile = profile  
    
    wfApp.Extensions.Add(stp)  
    
    // Add the custom tracking participant with a tracking profile  
    // that only emits tracking records for WriteLine activities.  
    StatusTrackingParticipant stp = new StatusTrackingParticipant  
    {  
        TrackingProfile = new TrackingProfile  
        {  
            Queries =
            {  
                new ActivityStateQuery  
                {  
                    ActivityName = "WriteLine",  
                    States = { ActivityStates.Executing },  
                    Arguments = { "Text" }  
                }  
            }  
        }  
    };  
    
    wfApp.Extensions.Add(stp);  
    

    この追跡プロファイルでは、WriteLine 状態の Executing アクティビティのアクティビティ状態レコードのみがカスタム追跡参加要素に出力されるように指定します。

    このコードを追加した後、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)  
    
        'Add the custom tracking participant with a tracking profile  
        'that only emits tracking records for WriteLine activities.  
        Dim query As New ActivityStateQuery()  
        query.ActivityName = "WriteLine"  
        query.States.Add(ActivityStates.Executing)  
        query.Arguments.Add("Text")  
    
        Dim profile As New TrackingProfile()  
        profile.Queries.Add(query)  
    
        Dim stp As New StatusTrackingParticipant()  
        stp.TrackingProfile = profile  
    
        wfApp.Extensions.Add(stp)  
    
        'Workflow lifecycle handlers...  
    
    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.  
        StringWriter sw = new StringWriter();  
        wfApp.Extensions.Add(sw);  
    
        // Add the custom tracking participant with a tracking profile  
        // that only emits tracking records for WriteLine activities.  
        StatusTrackingParticipant stp = new StatusTrackingParticipant  
        {  
            TrackingProfile = new TrackingProfile  
            {  
                Queries =
                {  
                    new ActivityStateQuery  
                    {  
                        ActivityName = "WriteLine",  
                        States = { ActivityStates.Executing },  
                        Arguments = { "Text" }  
                    }  
                }  
            }  
        };  
    
        wfApp.Extensions.Add(stp);  
    
        // Workflow lifecycle handlers...  
    

追跡情報を表示するには

  1. ソリューション エクスプローラーWorkflowHostForm を右クリックし、 [コードの表示] をクリックします。

  2. InstanceId_SelectedIndexChanged ハンドラーで、ステータス ウィンドウをクリアするコードの直後に次のコードを追加します。

    'If there is tracking data for this workflow, display it  
    'in the status window.  
    If File.Exists(WorkflowInstanceId.ToString()) Then  
        Dim status As String = File.ReadAllText(WorkflowInstanceId.ToString())  
        UpdateStatus(status)  
    End If  
    
    // If there is tracking data for this workflow, display it  
    // in the status window.  
    if (File.Exists(WorkflowInstanceId.ToString()))  
    {  
        string status = File.ReadAllText(WorkflowInstanceId.ToString());  
        UpdateStatus(status);  
    }  
    

    ワークフローの一覧で新しいワークフローを選択すると、そのワークフローの追跡レコードがステータス ウィンドウに読み込まれて表示されます。 完成した InstanceId_SelectedIndexChanged ハンドラーは次のようになります。

    Private Sub InstanceId_SelectedIndexChanged(sender As Object, e As EventArgs) Handles InstanceId.SelectedIndexChanged  
        If InstanceId.SelectedIndex = -1 Then  
            Return  
        End If  
    
        'Clear the status window.  
        WorkflowStatus.Clear()  
    
        'If there is tracking data for this workflow, display it  
        'in the status window.  
        If File.Exists(WorkflowInstanceId.ToString()) Then  
            Dim status As String = File.ReadAllText(WorkflowInstanceId.ToString())  
            UpdateStatus(status)  
        End If  
    
        '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  
    End Sub  
    
    private void InstanceId_SelectedIndexChanged(object sender, EventArgs e)  
    {  
        if (InstanceId.SelectedIndex == -1)  
        {  
            return;  
        }  
    
        // Clear the status window.  
        WorkflowStatus.Clear();  
    
        // If there is tracking data for this workflow, display it  
        // in the status window.  
        if (File.Exists(WorkflowInstanceId.ToString()))  
        {  
            string status = File.ReadAllText(WorkflowInstanceId.ToString());  
            UpdateStatus(status);  
        }  
    
        // 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();  
        }  
    }  
    

アプリケーションをビルドして実行するには

  1. Ctrl キーと Shift キーを押しながら B キーを押してアプリケーションをビルドします。

  2. Ctrl キーを押しながら F5 キーを押してアプリケーションを起動します。

  3. 推測ゲームの範囲と開始するワークフローの種類を選択し、 [新しいゲーム] をクリックします。 [推定] ボックスに推定値を入力し、 [ジャンプ] をクリックして推定値を送信します。 ワークフローの状態がステータス ウィンドウに表示されます。 この出力は、WriteLine アクティビティからキャプチャされます。 [ワークフローインスタンス Id] ボックスの一覧で別のワークフローを選択してそのワークフローに切り替えて、現在のワークフローの状態が削除されることを確認します。 以前のワークフローに戻して、次の例のように、状態が復元されることを確認します。

    注意

    追跡が有効になる前に開始されたワークフローに切り替えた場合、状態は表示されません。 ただし、推定値を追加すると、この時点では追跡が有効になっているため、その状態が保存されます。

    Please enter a number between 1 and 10
    Your guess is too high.
    Please enter a number between 1 and 10
    

    注意

    この情報は、乱数の範囲を決定する場合に役立ちますが、これまでに作成された推定値に関する情報を含んでいません。 この情報は、次のステップ「方法: ワークフローの複数のバージョンを同時にホストする」に記載されています。

    ワークフロー インスタンス ID を書き留め、ゲームを最後まで実行します。

  4. エクスプローラーを開き、NumberGuessWorkflowHost\bin\debug フォルダー (プロジェクトの設定によっては bin\release) に移動します。 プロジェクトの実行可能ファイルに加え、GUID ファイル名が付いたファイルがあることに注意します。 前の手順で完了したワークフローのワークフロー インスタンス ID に対応するファイルを特定してメモ帳で開きます。 追跡情報は、次のような情報が含まれています。

    Please enter a number between 1 and 10
    Your guess is too high.
    Please enter a number between 1 and 10
    Your guess is too high.
    Please enter a number between 1 and 10
    

    この追跡データには、ユーザーの推定値だけでなく、ワークフローの最後の推定値に関する情報も含まれていません。 これは、追跡情報がワークフローからの WriteLine 出力のみで構成されており、表示される最後のメッセージがワークフロー完了後に Completed ハンドラーから表示されるためです。 チュートリアルの次のステップ「方法: ワークフローの複数のバージョンを同時にホストする」では、ユーザーの推定値が表示されるように既存の WriteLine アクティビティを変更し、最終結果を表示する新しい WriteLine アクティビティを追加します。 これらの変更を統合した後、「方法: ワークフローの複数のバージョンを同時にホストする」で、ワークフローの複数のバージョンを同時にホストする方法について説明します。