Dela via


Skapa och köra ett långvarigt arbetsflöde

En av de centrala funktionerna i Windows Workflow Foundation (WF) är körningens möjlighet att spara och ta bort inaktiva arbetsflöden till en databas. Stegen i Så här gör du: Kör ett arbetsflöde visade grunderna i arbetsflödesvärd med hjälp av ett konsolprogram. Exempel på hur du startar arbetsflöden, livscykelhanterare för arbetsflöden och återupptar bokmärken. För att effektivt demonstrera arbetsflödets beständighet krävs en mer komplex arbetsflödesvärd som har stöd för att starta och återuppta flera arbetsflödesinstanser. Det här steget i självstudien visar hur du skapar ett värdprogram för Windows-formulär som stöder start och återupptagande av flera arbetsflödesinstanser, arbetsflödespersistence och utgör en grund för de avancerade funktioner som spårning och versionshantering som visas i efterföljande självstudier.

Kommentar

Det här självstudiesteget och de efterföljande stegen använder alla tre arbetsflödestyperna från Så här: Skapa ett arbetsflöde.

Skapa beständighetsdatabasen

  1. Öppna SQL Server Management Studio och anslut till den lokala servern, till exempel .\SQLEXPRESS. Högerklicka på noden Databaser på den lokala servern och välj Ny databas. Ge den nya databasen namnet WF45GettingStartedTutorial, acceptera alla andra värden och välj OK.

    Kommentar

    Kontrollera att du har behörigheten Skapa databas på den lokala servern innan du skapar databasen.

  2. Välj Öppna, ArkivArkiv-menyn . Bläddra till följande mapp: C:\Windows\Microsoft.NET\Framework\v4.0.30319\sql\en

    Välj följande två filer och klicka på Öppna.

    • SqlWorkflowInstanceStoreLogic.sql

    • SqlWorkflowInstanceStoreSchema.sql

  3. Välj SqlWorkflowInstanceStoreSchema.sqlmenyn Fönster . Kontrollera att WF45GettingStartedTutorial är markerat i listrutan Tillgängliga databaser och välj Kör frågemenyn.

  4. Välj SqlWorkflowInstanceStoreLogic.sqlmenyn Fönster . Kontrollera att WF45GettingStartedTutorial är markerat i listrutan Tillgängliga databaser och välj Kör frågemenyn.

    Varning

    Det är viktigt att utföra de föregående två stegen i rätt ordning. Om frågorna körs i fel ordning uppstår fel och beständighetsdatabasen är inte korrekt konfigurerad.

Så här lägger du till referensen till DurableInstancing-sammansättningarna

  1. Högerklicka på NumberGuessWorkflowHost i Solution Explorer och välj Lägg till referens.

  2. Välj Sammansättningar i listan Lägg till referens och skriv DurableInstancing i rutan Söksammansättningar . Detta filtrerar sammansättningarna och gör önskade referenser enklare att välja.

  3. Markera kryssrutan bredvid System.Activities.DurableInstancing och System.Runtime.DurableInstancing från listan Sökresultat och klicka på OK.

Så här skapar du arbetsflödets värdformulär

  1. Högerklicka på NumberGuessWorkflowHost i Solution Explorer och välj Lägg till nytt objekt.

  2. I listan Installerade mallar väljer du Windows-formulär, skriver WorkflowHostForm i rutan Namn och klickar på Lägg till.

  3. Konfigurera följande egenskaper i formuläret.

    Property Värde
    FormBorderStyle FixedSingle
    MaximizeBox Falsk
    Storlek 400, 420
  4. Lägg till följande kontroller i formuläret i den angivna ordningen och konfigurera egenskaperna enligt anvisningarna.

    Ctrl Egenskap: Värde
    Knapp Namn: NewGame

    Plats: 13, 13

    Storlek: 75, 23

    Text: Nytt spel
    Etikett Plats: 94, 18

    Text: Gissa ett tal från 1 till
    Kombinationsruta Namn: NumberRange

    DropDownStyle: Listruta

    Objekt: 10, 100, 1000

    Plats: 228, 12

    Storlek: 143, 21
    Etikett Plats: 13, 43

    Text: Arbetsflödestyp
    Kombinationsruta Namn: WorkflowType

    DropDownStyle: Listruta

    Objekt: StateMachineNumberGuessWorkflow, FlowchartNumberGuessWorkflow, SekventielltNumberGuessWorkflow

    Plats: 94, 40

    Storlek: 277, 21
    Etikett Namn: WorkflowVersion

    Plats: 13, 362

    Text: Arbetsflödesversion
    Gruppruta Plats: 13, 67

    Storlek: 358, 287

    Text: Spel

    Kommentar

    När du lägger till följande kontroller placerar du dem i GroupBox.

    Ctrl Egenskap: Värde
    Etikett Plats: 7, 20

    Text: Arbetsflödesinstans-ID
    Kombinationsruta Namn: InstanceId

    DropDownStyle: Listruta

    Plats: 121, 17

    Storlek: 227, 21
    Etikett Plats: 7, 47

    Text: Gissa
    TextBox Namn: Gissa

    Plats: 50, 44

    Storlek: 65, 20
    Knapp Namn: EnterGuess

    Plats: 121, 42

    Storlek: 75, 23

    Text: Ange Gissa
    Knapp Namn: QuitGame

    Plats: 274, 42

    Storlek: 75, 23

    Text: Avsluta
    TextBox Namn: WorkflowStatus

    Plats: 10, 73

    Flera linjer: Sant

    ReadOnly: Sant

    Rullningslister: Lodrätt

    Storlek: 338, 208
  5. Ange formulärets AcceptButton-egenskap till EnterGuess.

I följande exempel visas det ifyllda formuläret.

Skärmbild av ett arbetsflödesvärdformulär för Windows Workflow Foundation.

Så här lägger du till formulärets egenskaper och hjälpmetoder

Stegen i det här avsnittet lägger till egenskaper och hjälpmetoder i formulärklassen som konfigurerar användargränssnittet för formuläret för att stödja arbetsflöden för att köra och återuppta antal gissningar.

  1. Högerklicka på WorkflowHostForm i Solution Explorer och välj Visa kod.

  2. Lägg till följande using (eller Imports) -instruktioner överst i filen med de andra using (eller Imports) -uttrycken.

    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;
    
  3. Lägg till följande medlemsdeklarationer i klassen WorkflowHostForm .

    Viktigt!

    Microsoft rekommenderar att du använder det säkraste tillgängliga autentiseringsflödet. Om du ansluter till Azure SQL är hanterade identiteter för Azure-resurser den rekommenderade autentiseringsmetoden.

    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;
    

    Kommentar

    Om din anslutningssträng är annorlunda uppdaterar du connectionString för att referera till databasen.

  4. Lägg till en WorkflowInstanceId egenskap i WorkflowFormHost klassen.

    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;
        }
    }
    

    Kombinationsrutan InstanceId visar en lista över bevarade arbetsflödesinstans-ID:er och WorkflowInstanceId egenskapen returnerar det valda arbetsflödet.

  5. Lägg till en hanterare för formulärhändelsen Load . Om du vill lägga till hanteraren växlar du till Designvy för formuläret, klickar på ikonen Händelser överst i fönstret Egenskaper och dubbelklickar på Läs in.

    Private Sub WorkflowHostForm_Load(sender As Object, e As EventArgs) Handles Me.Load
    
    End Sub
    
    private void WorkflowHostForm_Load(object sender, EventArgs e)
    {
    
    }
    
  6. Lägg till följande kod i 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();
    

    När formuläret läses in SqlWorkflowInstanceStore konfigureras kombinationsrutorna för intervall och arbetsflödestyp till standardvärden och de bevarade arbetsflödesinstanserna läggs till i InstanceId kombinationsrutan.

  7. Lägg till en SelectedIndexChanged hanterare för InstanceId. Om du vill lägga till hanteraren växlar du till Designvy för formuläret, markerar InstanceId kombinationsrutan, klickar på ikonen Händelser överst i fönstret Egenskaper och dubbelklickar på SelectedIndexChanged.

    Private Sub InstanceId_SelectedIndexChanged(sender As Object, e As EventArgs) Handles InstanceId.SelectedIndexChanged
    
    End Sub
    
    private void InstanceId_SelectedIndexChanged(object sender, EventArgs e)
    {
    
    }
    
  8. Lägg till följande kod i InstanceId_SelectedIndexChanged. När användaren väljer ett arbetsflöde med hjälp av kombinationsrutan uppdaterar den här hanteraren statusfönstret.

    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();
    }
    
  9. Lägg till följande ListPersistedWorkflows metod i formulärklassen.

    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 frågar instansarkivet efter instanser av beständiga arbetsflöden och lägger till instans-ID:t i cboInstanceId kombinationsrutan.

  10. Lägg till följande UpdateStatus metod och motsvarande ombud i formulärklassen. Den här metoden uppdaterar statusfönstret i formuläret med statusen för det arbetsflöde som körs.

    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();
        }
    }
    
  11. Lägg till följande GameOver metod och motsvarande ombud i formulärklassen. När ett arbetsflöde har slutförts uppdaterar den här metoden formulärgränssnittet genom att ta bort instans-ID:t för det slutförda arbetsflödet från kombinationsrutan InstanceId .

    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;
        }
    }
    

Konfigurera instansarkivet, arbetsflödets livscykelhanterare och tillägg

  1. Lägg till en ConfigureWorkflowApplication metod i formulärklassen.

    Private Sub ConfigureWorkflowApplication(wfApp As WorkflowApplication)
    
    End Sub
    
    private void ConfigureWorkflowApplication(WorkflowApplication wfApp)
    {
    }
    

    Den här metoden konfigurerar WorkflowApplication, lägger till önskade tillägg och lägger till hanterare för arbetsflödets livscykelhändelser.

  2. I ConfigureWorkflowApplicationanger du SqlWorkflowInstanceStore för WorkflowApplication.

    ' Configure the persistence store.
    wfApp.InstanceStore = store
    
    // Configure the persistence store.
    wfApp.InstanceStore = store;
    
  3. Skapa sedan en StringWriter instans och lägg till den i Extensions samlingen av WorkflowApplication. När en StringWriter läggs till i tilläggen avbildas alla WriteLine aktivitetsutdata. När arbetsflödet blir inaktivt WriteLine kan utdata extraheras från StringWriter och visas i formuläret.

    ' 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);
    
  4. Lägg till följande hanterare för Completed händelsen. När ett arbetsflöde har slutförts visas antalet varv som gjorts för att gissa att talet visas i statusfönstret. Om arbetsflödet avslutas visas undantagsinformationen som orsakade avslutningen. I slutet av hanteraren GameOver anropas metoden, vilket tar bort det slutförda arbetsflödet från arbetsflödeslistan.

    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();
    };
    
  5. Lägg till följande Aborted och OnUnhandledException hanterare. Metoden GameOver anropas inte från Aborted hanteraren eftersom när en arbetsflödesinstans avbryts avslutas den inte och det går att återuppta instansen vid ett senare tillfälle.

    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;
    };
    
  6. Lägg till följande PersistableIdle hanterare. Den här hanteraren hämtar tillägget StringWriter som lades till, extraherar utdata från aktiviteterna WriteLine och visar det i statusfönstret.

    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;
    };
    

    Uppräkningen PersistableIdleAction har tre värden: None, Persistoch Unload. Persist gör att arbetsflödet bevaras, men det gör inte att arbetsflödet tas bort. Unload gör att arbetsflödet bevaras och tas bort.

    Följande exempel är den slutförda ConfigureWorkflowApplication metoden.

    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;
        };
    }
    

Aktivera start och återupptagning av flera arbetsflödestyper

För att kunna återuppta en arbetsflödesinstans måste värden ange arbetsflödesdefinitionen. I den här självstudien finns det tre arbetsflödestyper och efterföljande självstudiesteg introducerar flera versioner av dessa typer. WorkflowIdentity ger ett sätt för ett värdprogram att associera identifierande information med en bevarad arbetsflödesinstans. Stegen i det här avsnittet visar hur du skapar en verktygsklass som hjälper dig att mappa arbetsflödesidentiteten från en bevarad arbetsflödesinstans till motsvarande arbetsflödesdefinition. Mer information om WorkflowIdentity och versionshantering finns i Använda WorkflowIdentity och Versionshantering.

  1. Högerklicka på NumberGuessWorkflowHost i Solution Explorer och välj Lägg till, klass. Skriv WorkflowVersionMap i rutan Namn och klicka på Lägg till.

  2. Lägg till följande using eller Imports -instruktioner överst i filen med de andra using eller Imports -uttrycken.

    Imports System.Activities
    Imports NumberGuessWorkflowActivities
    
    using System.Activities;
    using NumberGuessWorkflowActivities;
    
  3. Ersätt klassdeklarationen WorkflowVersionMap med följande deklaration.

    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 innehåller tre arbetsflödesidentiteter som mappar till de tre arbetsflödesdefinitionerna från den här självstudien och används i följande avsnitt när arbetsflöden startas och återupptas.

Så här startar du ett nytt arbetsflöde

  1. Lägg till en Click hanterare för NewGame. Om du vill lägga till hanteraren växlar du till Designvy för formuläret och dubbelklickar på NewGame. En NewGame_Click hanterare läggs till och vyn växlar till kodvyn för formuläret. När användaren klickar på den här knappen startas ett nytt arbetsflöde.

    Private Sub NewGame_Click(sender As Object, e As EventArgs) Handles NewGame.Click
    
    End Sub
    
    private void NewGame_Click(object sender, EventArgs e)
    {
    
    }
    
  2. Lägg till följande kod i klickhanteraren. Den här koden skapar en ordlista med indataargument för arbetsflödet, med namnet på argumentet. Den här ordlistan har en post som innehåller intervallet för det slumpmässigt genererade talet som hämtats från intervallkombinationsrutan.

    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));
    
  3. Lägg sedan till följande kod som startar arbetsflödet. Arbetsflödesdefinitionen WorkflowIdentity och som motsvarar den typ av arbetsflöde som valts hämtas med hjälpklassen WorkflowVersionMap . Därefter skapas en ny WorkflowApplication instans med hjälp av arbetsflödesdefinitionen, WorkflowIdentityoch ordlistan med indataargument.

    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);
    
  4. Lägg sedan till följande kod som lägger till arbetsflödet i arbetsflödeslistan och visar arbetsflödets versionsinformation i formuläret.

    ' 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;
    
  5. Anropa ConfigureWorkflowApplication för att konfigurera instansarkivet, tilläggen och arbetsflödets livscykelhanterare för den här WorkflowApplication instansen.

    ' Configure the instance store, extensions, and
    ' workflow lifecycle handlers.
    ConfigureWorkflowApplication(wfApp)
    
    // Configure the instance store, extensions, and
    // workflow lifecycle handlers.
    ConfigureWorkflowApplication(wfApp);
    
  6. Anropa slutligen Run.

    ' Start the workflow.
    wfApp.Run()
    
    // Start the workflow.
    wfApp.Run();
    

    Följande exempel är den slutförda NewGame_Click hanteraren.

    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();
    }
    

Återuppta ett arbetsflöde

  1. Lägg till en Click hanterare för EnterGuess. Om du vill lägga till hanteraren växlar du till Designvy för formuläret och dubbelklickar på EnterGuess. När användaren klickar på den här knappen återupptas ett arbetsflöde.

    Private Sub EnterGuess_Click(sender As Object, e As EventArgs) Handles EnterGuess.Click
    
    End Sub
    
    private void EnterGuess_Click(object sender, EventArgs e)
    {
    
    }
    
  2. Lägg till följande kod för att säkerställa att ett arbetsflöde har valts i arbetsflödeslistan och att användarens gissning är giltig.

    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;
    }
    
  3. Hämta sedan den WorkflowApplicationInstance bevarade arbetsflödesinstansen. A WorkflowApplicationInstance representerar en instans av ett beständiga arbetsflöde som ännu inte har associerats med en arbetsflödesdefinition. I DefinitionIdentity innehåller WorkflowApplicationInstance den bevarade arbetsflödesinstansen WorkflowIdentity . I den här självstudien används verktygsklassen WorkflowVersionMap för att mappa WorkflowIdentity till rätt arbetsflödesdefinition. När arbetsflödesdefinitionen har hämtats skapas en WorkflowApplication med rätt arbetsflödesdefinition.

    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);
    
  4. När har WorkflowApplication skapats konfigurerar du instansarkivet, arbetsflödets livscykelhanterare och tillägg genom att anropa ConfigureWorkflowApplication. De här stegen måste utföras varje gång en ny WorkflowApplication skapas och de måste göras innan arbetsflödesinstansen WorkflowApplicationläses in i . När arbetsflödet har lästs in återupptas det med användarens gissning.

    ' 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);
    
  5. Slutligen rensar du textrutan gissa och förbereder formuläret för att acceptera en annan gissning.

    ' Clear the Guess textbox.
    Guess.Clear()
    Guess.Focus()
    
    // Clear the Guess textbox.
    Guess.Clear();
    Guess.Focus();
    

    Följande exempel är den slutförda EnterGuess_Click hanteraren.

    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();
    }
    

Så här avslutar du ett arbetsflöde

  1. Lägg till en Click hanterare för QuitGame. Om du vill lägga till hanteraren växlar du till Designvy för formuläret och dubbelklickar på QuitGame. När användaren klickar på den här knappen avslutas det valda arbetsflödet.

    Private Sub QuitGame_Click(sender As Object, e As EventArgs) Handles QuitGame.Click
    
    End Sub
    
    private void QuitGame_Click(object sender, EventArgs e)
    {
    
    }
    
  2. Lägg till följande kod i QuitGame_Click hanteraren. Den här koden kontrollerar först att ett arbetsflöde har valts i arbetsflödeslistan. Sedan läses den bevarade instansen DefinitionIdentity WorkflowApplicationin i , WorkflowApplicationInstanceanvänder för att fastställa rätt arbetsflödesdefinition och initierar sedan . Därefter konfigureras tilläggen och arbetsflödets livscykelhanterare med ett anrop till ConfigureWorkflowApplication. När har WorkflowApplication konfigurerats läses den in och anropas sedan 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.");
    

Skapa och köra programmet

  1. Dubbelklicka på Program.cs (eller Module1.vb) i Solution Explorer för att visa koden.

  2. Lägg till följande using (eller Imports) -instruktion överst i filen med de andra using (eller Imports) -uttrycken.

    Imports System.Windows.Forms
    
    using System.Windows.Forms;
    
  3. Ta bort eller kommentera ut det befintliga arbetsflödets värdkod från Så här kör du ett arbetsflöde och ersätt det med följande kod.

    Sub Main()
        Application.EnableVisualStyles()
        Application.Run(New WorkflowHostForm())
    End Sub
    
    static void Main(string[] args)
    {
        Application.EnableVisualStyles();
        Application.Run(new WorkflowHostForm());
    }
    
  4. Högerklicka på NumberGuessWorkflowHost i Solution Explorer och välj Egenskaper. På fliken Program anger du Windows-program för utdatatypen. Det här steget är valfritt, men om det inte följs visas konsolfönstret utöver formuläret.

  5. Tryck på Ctrl+Skift+B för att skapa programmet.

  6. Kontrollera att NumberGuessWorkflowHost har angetts som startprogram och tryck på Ctrl+F5 för att starta programmet.

  7. Välj ett intervall för gissningsspelet och vilken typ av arbetsflöde som ska startas och klicka på Nytt spel. Ange en gissning i rutan Gissa och klicka på för att skicka din gissning. Observera att utdata från aktiviteterna WriteLine visas i formuläret.

  8. Starta flera arbetsflöden med olika arbetsflödestyper och nummerintervall, ange några gissningar och växla mellan arbetsflödena genom att välja från listan Arbetsflödesinstans-ID .

    Observera att när du växlar till ett nytt arbetsflöde visas inte de tidigare gissningarna och förloppet för arbetsflödet i statusfönstret. Anledningen till att statusen inte är tillgänglig är att den inte samlas in och sparas någonstans. I nästa steg i självstudien How to: Create a Custom Tracking Participant (Gör så här: Skapa en anpassad spårningsdeltagare) skapar du en anpassad spårningsdeltagare som sparar den här informationen.