Jak utworzyć i uruchomić długotrwały przepływ pracy
Jedną z głównych funkcji programu Windows Workflow Foundation (WF) jest możliwość utrwalania i zwalniania bezczynnych przepływów pracy w bazie danych. Kroki opisane w temacie Instrukcje: Uruchamianie przepływu pracy przedstawiają podstawy hostingu przepływu pracy przy użyciu aplikacji konsolowej. Pokazano przykłady uruchamiania przepływów pracy, procedur obsługi cyklu życia przepływu pracy i wznawiania zakładek. Aby skutecznie zademonstrować trwałość przepływu pracy, wymagany jest bardziej złożony host przepływu pracy, który obsługuje uruchamianie i wznawianie wielu wystąpień przepływu pracy. W tym kroku w samouczku pokazano, jak utworzyć aplikację hosta formularzy systemu Windows, która obsługuje uruchamianie i wznawianie wielu wystąpień przepływu pracy, trwałość przepływu pracy i stanowi podstawę zaawansowanych funkcji, takich jak śledzenie i przechowywanie wersji, które przedstawiono w kolejnych krokach samouczka.
Uwaga
W tym kroku samouczka i kolejnych krokach są używane wszystkie trzy typy przepływów pracy z artykułu Instrukcje: Tworzenie przepływu pracy.
Aby utworzyć bazę danych trwałości
Otwórz program SQL Server Management Studio i połącz się z serwerem lokalnym, na przykład .\SQLEXPRESS. Kliknij prawym przyciskiem myszy węzeł Bazy danych na serwerze lokalnym, a następnie wybierz pozycję Nowa baza danych. Nadaj nowej bazie danych nazwę WF45GettingStartedTutorial, zaakceptuj wszystkie inne wartości i wybierz przycisk OK.
Uwaga
Przed utworzeniem bazy danych upewnij się, że masz uprawnienie Utwórz bazę danych na serwerze lokalnym.
Wybierz pozycję Otwórz, Plik z menu Plik . Przejdź do następującego folderu: C:\Windows\Microsoft.NET\Framework\v4.0.30319\sql\en
Wybierz następujące dwa pliki i kliknij przycisk Otwórz.
SqlWorkflowInstanceStoreLogic.sql
SqlWorkflowInstanceStoreSchema.sql
Wybierz SqlWorkflowInstanceStoreSchema.sql z menu Okno . Upewnij się, że na liście rozwijanej Dostępne bazy danych wybrano pozycję WF45GettingStartedTutorial i wybierz polecenie Wykonaj z menu Zapytanie.
Wybierz SqlWorkflowInstanceStoreLogic.sql z menu Okno . Upewnij się, że na liście rozwijanej Dostępne bazy danych wybrano pozycję WF45GettingStartedTutorial i wybierz polecenie Wykonaj z menu Zapytanie.
Ostrzeżenie
Ważne jest, aby wykonać poprzednie dwa kroki w prawidłowej kolejności. Jeśli zapytania są wykonywane poza kolejnością, występują błędy, a baza danych trwałości nie jest poprawnie skonfigurowana.
Aby dodać odwołanie do zestawów DurableInstancing
Kliknij prawym przyciskiem myszy pozycję NumberGuessWorkflowHost w Eksplorator rozwiązań i wybierz pozycję Dodaj odwołanie.
Wybierz pozycję Zestawy z listy Dodaj odwołanie i wpisz
DurableInstancing
ciąg w polu Wyszukaj zestawy . Spowoduje to filtrowanie zestawów i ułatwia wybór żądanych odwołań.Zaznacz pole wyboru obok pozycji System.Activities.DurableInstancing i System.Runtime.DurableInstancing z listy Wyniki wyszukiwania, a następnie kliknij przycisk OK.
Aby utworzyć formularz hosta przepływu pracy
Kliknij prawym przyciskiem myszy pozycję NumberGuessWorkflowHost w Eksplorator rozwiązań i wybierz pozycję Dodaj, Nowy element.
Na liście Zainstalowane szablony wybierz pozycję Formularz systemu Windows, wpisz
WorkflowHostForm
w polu Nazwa, a następnie kliknij przycisk Dodaj.Skonfiguruj następujące właściwości w formularzu.
Właściwości Wartość FormBorderStyle Naprawiono Maksymalizuj pole Fałsz Rozmiar 400, 420 Dodaj następujące kontrolki do formularza w określonej kolejności i skonfiguruj właściwości zgodnie z instrukcjami.
Kontrolka Właściwość: Wartość Przycisk Nazwa: NewGame
Lokalizacja: 13, 13
Rozmiar: 75, 23
Tekst: Nowa graEtykieta Lokalizacja: 94, 18
Tekst: Odgadnij liczbę z zakresu od 1 doComboBox Nazwa: NumberRange
DropDownStyle: Lista rozwijana
Elementy: 10, 100, 1000
Lokalizacja: 228, 12
Rozmiar: 143, 21Etykieta Lokalizacja: 13, 43
Tekst: Typ przepływu pracyComboBox Nazwa: WorkflowType
DropDownStyle: Lista rozwijana
Elementy: StateMachineNumberGuessWorkflow, FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflow
Lokalizacja: 94, 40
Rozmiar: 277, 21Etykieta Nazwa: WorkflowVersion
Lokalizacja: 13, 362
Tekst: wersja przepływu pracyGroupBox Lokalizacja: 13, 67
Rozmiar: 358, 287
Tekst: GraUwaga
Podczas dodawania następujących kontrolek umieść je w polu GroupBox.
Kontrolka Właściwość: Wartość Etykieta Lokalizacja: 7, 20
Tekst: identyfikator wystąpienia przepływu pracyComboBox Nazwa: InstanceId
DropDownStyle: Lista rozwijana
Lokalizacja: 121, 17
Rozmiar: 227, 21Etykieta Lokalizacja: 7, 47
Tekst: ZgadnijTextBox Nazwa: Odgadnij
Lokalizacja: 50, 44
Rozmiar: 65, 20Przycisk Nazwa: EnterGuess
Lokalizacja: 121, 42
Rozmiar: 75, 23
Tekst: Wprowadź wartość GuessPrzycisk Nazwa: QuitGame
Lokalizacja: 274, 42
Rozmiar: 75, 23
Tekst: ZamknijTextBox Nazwa: WorkflowStatus
Lokalizacja: 10, 73
Wielowierszowa: prawda
ReadOnly: Prawda
Paski przewijania: pionowy
Rozmiar: 338, 208Ustaw właściwość AcceptButton formularza na EnterGuess.
Poniższy przykład ilustruje wypełniony formularz.
Aby dodać właściwości i metody pomocnicze formularza
Kroki opisane w tej sekcji dodają właściwości i metody pomocnicze do klasy formularzy, które konfigurują interfejs użytkownika formularza w celu obsługi uruchamiania i wznawiania przepływów pracy zgadywania liczb.
Kliknij prawym przyciskiem myszy pozycję WorkflowHostForm w Eksplorator rozwiązań i wybierz polecenie Wyświetl kod.
Dodaj następujące
using
instrukcje (lubImports
) w górnej części pliku z innymiusing
instrukcjami (lubImports
).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;
Dodaj następujące deklaracje składowe do klasy WorkflowHostForm .
Ważne
Firma Microsoft zaleca korzystanie z najbezpieczniejszego dostępnego przepływu uwierzytelniania. Jeśli łączysz się z usługą Azure SQL, tożsamości zarządzane dla zasobów platformy Azure to zalecana metoda uwierzytelniania.
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;
Uwaga
Jeśli parametry połączenia jest inna, zaktualizuj ją
connectionString
, aby odwołać się do bazy danych.WorkflowInstanceId
Dodaj właściwość doWorkflowFormHost
klasy.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; } }
W
InstanceId
polu kombi zostanie wyświetlona lista identyfikatorów utrwalonego wystąpienia przepływu pracy, aWorkflowInstanceId
właściwość zwraca aktualnie wybrany przepływ pracy.Dodaj procedurę obsługi dla zdarzenia formularza
Load
. Aby dodać procedurę obsługi, przejdź do widoku projektu dla formularza, kliknij ikonę Zdarzenia w górnej części okna Właściwości , a następnie kliknij dwukrotnie pozycję Załaduj.Private Sub WorkflowHostForm_Load(sender As Object, e As EventArgs) Handles Me.Load End Sub
private void WorkflowHostForm_Load(object sender, EventArgs e) { }
Dodaj następujący kod do pliku
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();
Po załadowaniu
SqlWorkflowInstanceStore
formularza zostanie skonfigurowane pola kombi zakresu i typu przepływu pracy są ustawione na wartości domyślne, a utrwalone wystąpienia przepływu pracy są dodawane doInstanceId
pola kombi.Dodaj procedurę
SelectedIndexChanged
obsługi dla elementuInstanceId
. Aby dodać procedurę obsługi, przejdź do widoku projektu dla formularza, zaznaczInstanceId
pole kombi, kliknij ikonę Zdarzenia w górnej części okna Właściwości , a następnie kliknij dwukrotnie polecenie SelectedIndexChanged.Private Sub InstanceId_SelectedIndexChanged(sender As Object, e As EventArgs) Handles InstanceId.SelectedIndexChanged End Sub
private void InstanceId_SelectedIndexChanged(object sender, EventArgs e) { }
Dodaj następujący kod do pliku
InstanceId_SelectedIndexChanged
. Za każdym razem, gdy użytkownik wybierze przepływ pracy przy użyciu pola kombi, program obsługi aktualizuje okno stanu.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(); }
Dodaj następującą
ListPersistedWorkflows
metodę do klasy formularza.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
wysyła zapytanie do magazynu wystąpień utrwalonego przepływu pracy i dodaje identyfikatory wystąpień docboInstanceId
pola kombi.Dodaj następującą
UpdateStatus
metodę i odpowiedni delegat do klasy formularza. Ta metoda aktualizuje okno stanu w formularzu ze stanem aktualnie uruchomionego przepływu pracy.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(); } }
Dodaj następującą
GameOver
metodę i odpowiedni delegat do klasy formularza. Po zakończeniu przepływu pracy ta metoda aktualizuje interfejs użytkownika formularza, usuwając identyfikator wystąpienia ukończonego przepływu pracy z pola kombi 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; } }
Aby skonfigurować magazyn wystąpień, programy obsługi cyklu życia przepływu pracy i rozszerzenia
Dodaj metodę
ConfigureWorkflowApplication
do klasy formularza.Private Sub ConfigureWorkflowApplication(wfApp As WorkflowApplication) End Sub
private void ConfigureWorkflowApplication(WorkflowApplication wfApp) { }
Ta metoda konfiguruje
WorkflowApplication
element , dodaje żądane rozszerzenia i dodaje programy obsługi dla zdarzeń cyklu życia przepływu pracy.W
ConfigureWorkflowApplication
pliku określ wartośćSqlWorkflowInstanceStore
dla elementuWorkflowApplication
.' Configure the persistence store. wfApp.InstanceStore = store
// Configure the persistence store. wfApp.InstanceStore = store;
Następnie utwórz
StringWriter
wystąpienie i dodaj je doExtensions
kolekcji .WorkflowApplication
Po dodaniu elementuStringWriter
do rozszerzeń przechwytuje wszystkieWriteLine
dane wyjściowe działania. Gdy przepływ pracy stanie się bezczynny,WriteLine
dane wyjściowe można wyodrębnić zStringWriter
elementu i wyświetlanego w formularzu.' 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);
Dodaj następującą procedurę
Completed
obsługi dla zdarzenia. Po pomyślnym zakończeniu przepływu pracy liczba zmieni się, aby odgadnąć, że liczba zostanie wyświetlona w oknie stanu. Jeśli przepływ pracy zakończy się, zostaną wyświetlone informacje o wyjątku, które spowodowały zakończenie. Na końcu procedury obsługi wywoływanaGameOver
jest metoda, która usuwa ukończony przepływ pracy z listy przepływów pracy.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(); };
Dodaj następujące
Aborted
programy obsługi iOnUnhandledException
. MetodaGameOver
nie jest wywoływana zAborted
programu obsługi, ponieważ po przerwaniu wystąpienia przepływu pracy nie zostanie przerwane i można wznowić wystąpienie w późniejszym czasie.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; };
Dodaj następującą
PersistableIdle
procedurę obsługi. Ta procedura obsługi pobieraStringWriter
dodane rozszerzenie, wyodrębnia dane wyjściowe zWriteLine
działań i wyświetla je w oknie stanu.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; };
Wyliczenie PersistableIdleAction ma trzy wartości: None, Persisti Unload. Persist powoduje, że przepływ pracy będzie się powtarzać, ale nie powoduje zwolnienia przepływu pracy. Unload powoduje utrwalanie i zwalnianie przepływu pracy.
Poniższy przykład to ukończona
ConfigureWorkflowApplication
metoda.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; }; }
Aby włączyć uruchamianie i wznawianie wielu typów przepływów pracy
Aby wznowić wystąpienie przepływu pracy, host musi podać definicję przepływu pracy. W tym samouczku istnieją trzy typy przepływów pracy, a kolejne kroki samouczka przedstawiają wiele wersji tego typu. WorkflowIdentity
program umożliwia aplikacji hosta skojarzenie informacji identyfikujących z utrwalonego wystąpienia przepływu pracy. W krokach w tej sekcji pokazano, jak utworzyć klasę narzędzi, aby ułatwić mapowanie tożsamości przepływu pracy z utrwalonego wystąpienia przepływu pracy na odpowiednią definicję przepływu pracy. Aby uzyskać więcej informacji na temat WorkflowIdentity
i przechowywania wersji, zobacz Using WorkflowIdentity and Versioning (Używanie identyfikatorów przepływu pracy i przechowywania wersji).
Kliknij prawym przyciskiem myszy pozycję NumberGuessWorkflowHost w Eksplorator rozwiązań i wybierz polecenie Dodaj, Klasa. Wpisz
WorkflowVersionMap
w polu Nazwa i kliknij przycisk Dodaj.Dodaj następujące
using
instrukcje lubImports
w górnej części pliku z innymiusing
instrukcjami lubImports
.Imports System.Activities Imports NumberGuessWorkflowActivities
using System.Activities; using NumberGuessWorkflowActivities;
Zastąp deklarację
WorkflowVersionMap
klasy następującą deklaracją.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
Zawiera trzy tożsamości przepływu pracy, które są mapowane na trzy definicje przepływu pracy z tego samouczka i są używane w poniższych sekcjach, gdy przepływy pracy są uruchamiane i wznawiane.
Aby uruchomić nowy przepływ pracy
Dodaj procedurę
Click
obsługi dla elementuNewGame
. Aby dodać procedurę obsługi, przejdź do widoku projektu dla formularza, a następnie kliknij dwukrotnie pozycjęNewGame
. Dodano proceduręNewGame_Click
obsługi, a widok przełącza się do widoku kodu dla formularza. Za każdym razem, gdy użytkownik kliknie ten przycisk, zostanie uruchomiony nowy przepływ pracy.Private Sub NewGame_Click(sender As Object, e As EventArgs) Handles NewGame.Click End Sub
private void NewGame_Click(object sender, EventArgs e) { }
Dodaj następujący kod do procedury obsługi kliknięć. Ten kod tworzy słownik argumentów wejściowych dla przepływu pracy, do których kluczem jest nazwa argumentu. Ten słownik zawiera jeden wpis zawierający zakres losowo wygenerowanej liczby pobranej z pola kombi zakresu.
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));
Następnie dodaj następujący kod, który uruchamia przepływ pracy. Definicja
WorkflowIdentity
przepływu pracy i odpowiadająca wybranemuWorkflowVersionMap
typowi przepływu pracy jest pobierana przy użyciu klasy pomocnika. Następnie zostanie utworzone noweWorkflowApplication
wystąpienie przy użyciu definicji przepływu pracy,WorkflowIdentity
i słownika argumentów wejściowych.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);
Następnie dodaj następujący kod, który dodaje przepływ pracy do listy przepływów pracy i wyświetla informacje o wersji przepływu pracy w formularzu.
' 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;
Wywołaj metodę
ConfigureWorkflowApplication
konfigurowania magazynu wystąpień, rozszerzeń i procedur obsługi cyklu życia przepływu pracy dla tegoWorkflowApplication
wystąpienia.' Configure the instance store, extensions, and ' workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp)
// Configure the instance store, extensions, and // workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp);
Na koniec wywołaj metodę
Run
.' Start the workflow. wfApp.Run()
// Start the workflow. wfApp.Run();
Poniższy przykład to ukończona
NewGame_Click
procedura obsługi.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(); }
Aby wznowić przepływ pracy
Dodaj procedurę
Click
obsługi dla elementuEnterGuess
. Aby dodać procedurę obsługi, przejdź do widoku projektu dla formularza, a następnie kliknij dwukrotnie pozycjęEnterGuess
. Za każdym razem, gdy użytkownik kliknie ten przycisk, zostanie wznowiony przepływ pracy.Private Sub EnterGuess_Click(sender As Object, e As EventArgs) Handles EnterGuess.Click End Sub
private void EnterGuess_Click(object sender, EventArgs e) { }
Dodaj następujący kod, aby upewnić się, że przepływ pracy został wybrany na liście przepływów pracy i czy odgadnięcie użytkownika jest prawidłowe.
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; }
Następnie pobierz
WorkflowApplicationInstance
utrwalone wystąpienie przepływu pracy. ObiektWorkflowApplicationInstance
reprezentuje utrwalone wystąpienie przepływu pracy, które nie zostało jeszcze skojarzone z definicją przepływu pracy. ElementDefinitionIdentity
zawieraWorkflowApplicationInstance
WorkflowIdentity
wystąpienie utrwalonego przepływu pracy. W tym samouczkuWorkflowVersionMap
klasa narzędzia jest używana do mapowania elementuWorkflowIdentity
na poprawną definicję przepływu pracy. Po pobraniuWorkflowApplication
definicji przepływu pracy zostanie utworzona wartość , używając poprawnej definicji przepływu pracy.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);
Po utworzeniu
WorkflowApplication
programu skonfiguruj magazyn wystąpień, programy obsługi cyklu życia przepływu pracy i rozszerzenia, wywołując metodęConfigureWorkflowApplication
. Te kroki należy wykonać za każdym razem, gdy zostanie utworzony nowyWorkflowApplication
, i należy je wykonać przed załadowaniem wystąpienia przepływu pracy do klasyWorkflowApplication
. Po załadowaniu przepływu pracy zostanie wznowiony zgadywaniem użytkownika.' 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);
Na koniec wyczyść pole tekstowe zgadywania i przygotuj formularz do zaakceptowania innego zgadnięcia.
' Clear the Guess textbox. Guess.Clear() Guess.Focus()
// Clear the Guess textbox. Guess.Clear(); Guess.Focus();
Poniższy przykład to ukończona
EnterGuess_Click
procedura obsługi.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(); }
Aby zakończyć przepływ pracy
Dodaj procedurę
Click
obsługi dla elementuQuitGame
. Aby dodać procedurę obsługi, przejdź do widoku projektu dla formularza, a następnie kliknij dwukrotnie pozycjęQuitGame
. Za każdym razem, gdy użytkownik kliknie ten przycisk, aktualnie wybrany przepływ pracy zostanie zakończony.Private Sub QuitGame_Click(sender As Object, e As EventArgs) Handles QuitGame.Click End Sub
private void QuitGame_Click(object sender, EventArgs e) { }
Dodaj następujący kod do
QuitGame_Click
programu obsługi. Ten kod najpierw sprawdza, czy na liście przepływów pracy wybrano przepływ pracy. Następnie ładuje utrwalone wystąpienie doWorkflowApplicationInstance
klasy , używaDefinitionIdentity
elementu , aby określić poprawną definicję przepływu pracy, a następnie inicjujeWorkflowApplication
element . Następnie rozszerzenia i procedury obsługi cyklu życia przepływu pracy są konfigurowane za pomocą wywołania metodyConfigureWorkflowApplication
. Po skonfigurowaniuWorkflowApplication
jest ładowana, a następnieTerminate
wywoływana.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.");
Aby skompilować i uruchomić aplikację
Kliknij dwukrotnie Program.cs (lub Module1.vb) w Eksplorator rozwiązań, aby wyświetlić kod.
Dodaj następującą
using
instrukcję (lubImports
) w górnej części pliku z innymiusing
instrukcjami (lubImports
).Imports System.Windows.Forms
using System.Windows.Forms;
Usuń istniejący kod hostingu przepływu pracy lub oznacz go jako komentarz z sekcji Instrukcje: Uruchamianie przepływu pracy i zastąp go następującym kodem.
Sub Main() Application.EnableVisualStyles() Application.Run(New WorkflowHostForm()) End Sub
static void Main(string[] args) { Application.EnableVisualStyles(); Application.Run(new WorkflowHostForm()); }
Kliknij prawym przyciskiem myszy pozycję NumberGuessWorkflowHost w Eksplorator rozwiązań i wybierz polecenie Właściwości. Na karcie Aplikacja określ wartość Aplikacja systemu Windows dla typu dane wyjściowe. Ten krok jest opcjonalny, ale jeśli nie jest on obserwowany, okno konsoli jest wyświetlane oprócz formularza.
Naciśnij Ctrl+Shift+B, aby skompilować aplikację.
Upewnij się, że parametr NumberGuessWorkflowHost jest ustawiony jako aplikacja startowa, a następnie naciśnij Ctrl+F5, aby uruchomić aplikację.
Wybierz zakres dla gry zgadywania i typu przepływu pracy do uruchomienia, a następnie kliknij pozycję Nowa gra. Wprowadź wartość zgadnij w polu Odgadnij i kliknij przycisk Przejdź, aby przesłać swoje odgadnięcie. Zwróć uwagę, że dane wyjściowe z
WriteLine
działań są wyświetlane w formularzu.Uruchom kilka przepływów pracy przy użyciu różnych typów przepływów pracy i zakresów liczb, wprowadź pewne odgadnięcia i przełącz się między przepływami pracy, wybierając z listy Identyfikator wystąpienia przepływu pracy.
Pamiętaj, że po przełączeniu się do nowego przepływu pracy poprzednie odgadnięcia i postęp przepływu pracy nie są wyświetlane w oknie stanu. Przyczyną niedostępnego stanu jest to, że nie jest przechwytywany i zapisywany w dowolnym miejscu. W następnym kroku samouczka , Jak utworzyć uczestnika niestandardowego śledzenia, utworzysz niestandardowego uczestnika śledzenia, który zapisuje te informacje.