Vytvoření a spuštění dlouhotrvajícího pracovního postupu
Jednou z hlavních funkcí windows Workflow Foundation (WF) je schopnost modulu runtime zachovat a uvolnit nečinné pracovní postupy do databáze. Kroky v části Postupy: Spuštění pracovního postupu demonstrovalo základy hostování pracovních postupů pomocí konzolové aplikace. Příklady spouštění pracovních postupů, obslužných rutin životního cyklu pracovního postupu a obnovení záložek Aby bylo možné efektivně předvést trvalost pracovního postupu, je vyžadován složitější hostitel pracovního postupu, který podporuje spouštění a obnovování více instancí pracovního postupu. Tento krok v tomto kurzu ukazuje, jak vytvořit hostitelskou aplikaci formuláře Windows, která podporuje spouštění a obnovení více instancí pracovního postupu, trvalost pracovních postupů a poskytuje základ pro pokročilé funkce, jako je sledování a správa verzí, které jsou demonstrována v dalších krocích kurzu.
Poznámka:
Tento krok kurzu a následné kroky používají všechny tři typy pracovních postupů: Vytvoření pracovního postupu.
Vytvoření databáze trvalosti
Otevřete APLIKACI SQL Server Management Studio a připojte se k místnímu serveru, například .\SQLEXPRESS. Klikněte pravým tlačítkem myši na uzel Databáze na místním serveru a vyberte Nová databáze. Pojmenujte novou databázi WF45GettingStartedTutorial, přijměte všechny ostatní hodnoty a vyberte OK.
Poznámka:
Před vytvořením databáze se ujistěte, že máte na místním serveru oprávnění k vytvoření databáze.
V nabídce Soubor zvolte Otevřít, Soubor. Přejděte do následující složky: C:\Windows\Microsoft.NET\Framework\v4.0.30319\sql\en
Vyberte následující dva soubory a klikněte na Otevřít.
SqlWorkflowInstanceStoreLogic.sql
SqlWorkflowInstanceStoreSchema.sql
V nabídce Okno zvolte SqlWorkflowInstanceStoreSchema.sql. Ujistěte se, že je v rozevíracím seznamu Dostupné databáze vybraná možnost WF45GettingStartedTutorial a v nabídce Dotaz zvolte Spustit.
V nabídce Okno zvolte SqlWorkflowInstanceStoreLogic.sql. Ujistěte se, že je v rozevíracím seznamu Dostupné databáze vybraná možnost WF45GettingStartedTutorial a v nabídce Dotaz zvolte Spustit.
Upozorňující
Je důležité provést předchozí dva kroky ve správném pořadí. Pokud jsou dotazy prováděny mimo pořadí, dojde k chybám a databáze trvalosti není správně nakonfigurovaná.
Přidání odkazu na sestavení DurableInstancing
Klikněte pravým tlačítkem na NumberGuessWorkflowHost v Průzkumník řešení a vyberte Přidat odkaz.
V seznamu Přidat odkaz vyberte sestavení a zadejte
DurableInstancing
do pole Hledat sestavení. Tím se vyfiltrují sestavení a usnadní se výběr požadovaných odkazů.Zaškrtněte políčko vedle položky System.Activities.DurableInstancing a System.Runtime.DurableInstancing ze seznamu výsledků hledání a klepněte na tlačítko OK.
Vytvoření formuláře hostitele pracovního postupu
Klikněte pravým tlačítkem na NumberGuessWorkflowHost v Průzkumník řešení a zvolte Přidat, Nová položka.
V seznamu Nainstalované šablony zvolte Formulář Systému Windows, zadejte
WorkflowHostForm
do pole Název a klepněte na tlačítko Přidat.Ve formuláři nakonfigurujte následující vlastnosti.
Vlastnost Hodnota FormBorderStyle Pevná single MaximalizovatBox False Velikost 400, 420 Do formuláře v uvedeném pořadí přidejte následující ovládací prvky a nakonfigurujte vlastnosti podle pokynů.
Ovládací prvek Vlastnost: Hodnota Tlačítko Název: NewGame
Umístění: 13, 13
Velikost: 75, 23
Text: Nová hraPopisek Umístění: 94, 18
Text: Odhad čísla od 1 doComboBox Název: NumberRange
DropDownStyle: DropDownList
Položky: 10, 100, 1000
Umístění: 228, 12
Velikost: 143, 21Popisek Umístění: 13, 43
Text: Typ pracovního postupuComboBox Název: WorkflowType
DropDownStyle: DropDownList
Položky: StateMachineNumberGuessWorkflow, FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflow
Umístění: 94, 40
Velikost: 277, 21Popisek Název: WorkflowVersion
Umístění: 13, 362
Text: Verze pracovního postupuGroupBox Umístění: 13, 67
Velikost: 358, 287
Text: HraPoznámka:
Při přidávání následujících ovládacích prvků je vložte do GroupBoxu.
Ovládací prvek Vlastnost: Hodnota Popisek Umístění: 7, 20
Text: ID instance pracovního postupuComboBox Název: InstanceId
DropDownStyle: DropDownList
Umístění: 121, 17
Velikost: 227, 21Popisek Umístění: 7, 47
Text: OdhadTextBox Název: Odhad
Umístění: 50, 44
Velikost: 65, 20Tlačítko Název: EnterGuess
Umístění: 121, 42
Velikost: 75, 23
Text: Zadejte odhad.Tlačítko Název: QuitGame
Umístění: 274, 42
Velikost: 75, 23
Text: UkončitTextBox Název: WorkflowStatus
Umístění: 10, 73
Víceřádkový: True
ReadOnly: True
Posuvníky: Svisle
Velikost: 338, 208Nastavte AcceptButton vlastnost formuláře EnterGuess.
Následující příklad znázorňuje dokončený formulář.
Přidání vlastností a pomocných metod formuláře
Kroky v této části přidávají vlastnosti a pomocné metody do třídy formuláře, které konfigurují uživatelské rozhraní formuláře tak, aby podporovaly spouštění a obnovení pracovních postupů odhadu čísel.
Pravým tlačítkem myši klikněte na WorkflowHostForm v Průzkumník řešení a zvolte Zobrazit kód.
Do horní části souboru přidejte následující
using
příkazy (neboImports
) s jinýmiusing
příkazy (neboImports
).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;
Do třídy WorkflowHostForm přidejte následující deklarace členů.
Důležité
Microsoft doporučuje používat nejbezpečnější dostupný tok ověřování. Pokud se připojujete k Azure SQL, spravované identity pro prostředky Azure se doporučují metodou ověřování.
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;
Poznámka:
Pokud se vaše připojovací řetězec liší, aktualizujte
connectionString
databázi.WorkflowInstanceId
Přidejte do třídy vlastnostWorkflowFormHost
.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; } }
Pole
InstanceId
se seznamem zobrazí seznam id trvalých instancí pracovního postupu aWorkflowInstanceId
vlastnost vrátí aktuálně vybraný pracovní postup.Přidejte obslužnou rutinu události formuláře
Load
. Pokud chcete přidat obslužnou rutinu, přepněte do návrhového zobrazení formuláře, klikněte v horní části okna Vlastnosti na ikonu Události a poklikejte na Načíst.Private Sub WorkflowHostForm_Load(sender As Object, e As EventArgs) Handles Me.Load End Sub
private void WorkflowHostForm_Load(object sender, EventArgs e) { }
Přidejte následující kód do
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();
Když se formulář načte, nakonfiguruje se
SqlWorkflowInstanceStore
pole se seznamem rozsahu a typu pracovního postupu na výchozí hodnoty a trvalé instance pracovního postupu se přidají do pole se seznamemInstanceId
.Přidání obslužné rutiny
SelectedIndexChanged
proInstanceId
. Chcete-li přidat obslužnou rutinu, přepněte do návrhového zobrazení formuláře, vyberteInstanceId
pole se seznamem, klikněte na ikonu Události v horní části okna Vlastnosti a poklikejte na SelectedIndexChanged.Private Sub InstanceId_SelectedIndexChanged(sender As Object, e As EventArgs) Handles InstanceId.SelectedIndexChanged End Sub
private void InstanceId_SelectedIndexChanged(object sender, EventArgs e) { }
Přidejte následující kód do
InstanceId_SelectedIndexChanged
. Kdykoli uživatel vybere pracovní postup pomocí pole se seznamem, tato obslužná rutina aktualizuje stavové okno.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(); }
Do třídy formuláře přidejte následující
ListPersistedWorkflows
metodu.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
dotazuje úložiště instancí pro trvalé instance pracovního postupu a přidá ID instance do pole se seznamemcboInstanceId
.Přidejte následující
UpdateStatus
metodu a odpovídající delegáta do třídy formuláře. Tato metoda aktualizuje stavové okno ve formuláři se stavem aktuálně spuštěného pracovního postupu.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(); } }
Přidejte následující
GameOver
metodu a odpovídající delegáta do třídy formuláře. Po dokončení pracovního postupu tato metoda aktualizuje uživatelské rozhraní formuláře odebráním ID instance dokončeného pracovního postupu ze pole se seznamem 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; } }
Konfigurace úložiště instancí, obslužných rutin životního cyklu pracovního postupu a rozšíření
Přidejte metodu
ConfigureWorkflowApplication
do třídy formuláře.Private Sub ConfigureWorkflowApplication(wfApp As WorkflowApplication) End Sub
private void ConfigureWorkflowApplication(WorkflowApplication wfApp) { }
Tato metoda nakonfiguruje
WorkflowApplication
, přidá požadovaná rozšíření a přidá obslužné rutiny pro události životního cyklu pracovního postupu.Do
ConfigureWorkflowApplication
poleSqlWorkflowInstanceStore
zadejte hodnotu .WorkflowApplication
' Configure the persistence store. wfApp.InstanceStore = store
// Configure the persistence store. wfApp.InstanceStore = store;
Dále vytvořte
StringWriter
instanci a přidejte ji doExtensions
kolekce objektuWorkflowApplication
.StringWriter
Když se přidá do rozšíření, zachytí se veškerýWriteLine
výstup aktivity. Když se pracovní postup změní na nečinný,WriteLine
můžete výstup extrahovat zStringWriter
formuláře a zobrazit ho.' 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);
Přidejte následující obslužnou rutinu
Completed
události. Po úspěšném dokončení pracovního postupu se v okně stavu zobrazí počet provedených kroků, aby bylo odhadnuto číslo. Pokud pracovní postup skončí, zobrazí se informace o výjimce, které způsobily ukončení. Na konci obslužné rutinyGameOver
je volána metoda, která odebere dokončený pracovní postup ze seznamu pracovních postupů.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(); };
Přidejte následující
Aborted
aOnUnhandledException
obslužné rutiny. MetodaGameOver
není volána zAborted
obslužné rutiny, protože když je instance pracovního postupu přerušena, neukončí a je možné obnovit instanci později.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; };
Přidejte následující
PersistableIdle
obslužnou rutinu. Tato obslužná rutinaStringWriter
načte přidané rozšíření, extrahuje výstup zWriteLine
aktivit a zobrazí ho v okně stavu.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; };
Výčet PersistableIdleAction má tři hodnoty: None, Persista Unload. Persist způsobí zachování pracovního postupu, ale nezpůsobí uvolnění pracovního postupu. Unload způsobí trvalé a uvolněné pracovní postup.
Následující příklad je dokončená
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; }; }
Povolení spuštění a obnovení více typů pracovních postupů
Aby bylo možné obnovit instanci pracovního postupu, musí hostitel zadat definici pracovního postupu. V tomto kurzu existují tři typy pracovních postupů a následné kroky kurzu představují více verzí těchto typů. WorkflowIdentity
poskytuje způsob, jak hostitelská aplikace přidružit identifikaci informací k trvalé instanci pracovního postupu. Kroky v této části ukazují, jak vytvořit třídu nástrojů, která vám pomůže s mapováním identity pracovního postupu z trvalé instance pracovního postupu na odpovídající definici pracovního postupu. Další informace o WorkflowIdentity
správě verzí a správu verzí naleznete v tématu Použití WorkflowIdentity a správy verzí.
Klikněte pravým tlačítkem na NumberGuessWorkflowHost v Průzkumník řešení a zvolte Přidat, Třída. Zadejte
WorkflowVersionMap
do pole Název a klikněte na Přidat.Do horní části souboru přidejte následující
using
příkazy neboImports
příkazy s jinýmiusing
příkazy neboImports
příkazy.Imports System.Activities Imports NumberGuessWorkflowActivities
using System.Activities; using NumberGuessWorkflowActivities;
WorkflowVersionMap
Nahraďte deklaraci třídy následující deklarací.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
obsahuje tři identity pracovního postupu, které se mapují na tři definice pracovního postupu z tohoto kurzu, a používá se v následujících částech při spuštění a obnovení pracovních postupů.
Zahájení nového pracovního postupu
Přidání obslužné rutiny
Click
proNewGame
. Chcete-li přidat obslužnou rutinu, přepněte do návrhového zobrazení formuláře a poklikejte naNewGame
tlačítko . Přidá se obslužnáNewGame_Click
rutina a zobrazení se přepne do zobrazení kódu formuláře. Pokaždé, když uživatel klikne na toto tlačítko, spustí se nový pracovní postup.Private Sub NewGame_Click(sender As Object, e As EventArgs) Handles NewGame.Click End Sub
private void NewGame_Click(object sender, EventArgs e) { }
Do obslužné rutiny kliknutí přidejte následující kód. Tento kód vytvoří slovník vstupníchargumentch Tento slovník obsahuje jednu položku, která obsahuje oblast náhodně vygenerovaného čísla načteného z pole se seznamem rozsahu.
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));
Dále přidejte následující kód, který spouští pracovní postup. Definice
WorkflowIdentity
pracovního postupu odpovídající vybranému typu pracovního postupu se načte pomocíWorkflowVersionMap
pomocné třídy. Dále se vytvoří nováWorkflowApplication
instance pomocí definiceWorkflowIdentity
pracovního postupu a slovníku vstupních argumentů.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);
Dále přidejte následující kód, který přidá pracovní postup do seznamu pracovních postupů a zobrazí informace o verzi pracovního postupu ve formuláři.
' 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;
Volání
ConfigureWorkflowApplication
konfigurace úložiště instancí, rozšíření a obslužných rutin životního cyklu pracovního postupu pro tutoWorkflowApplication
instanci' Configure the instance store, extensions, and ' workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp)
// Configure the instance store, extensions, and // workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp);
Nakonec zavolejte
Run
.' Start the workflow. wfApp.Run()
// Start the workflow. wfApp.Run();
Následující příklad je dokončená obslužná
NewGame_Click
rutina.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(); }
Obnovení pracovního postupu
Přidání obslužné rutiny
Click
proEnterGuess
. Chcete-li přidat obslužnou rutinu, přepněte do návrhového zobrazení formuláře a poklikejte naEnterGuess
tlačítko . Kdykoli uživatel klikne na toto tlačítko, pracovní postup se obnoví.Private Sub EnterGuess_Click(sender As Object, e As EventArgs) Handles EnterGuess.Click End Sub
private void EnterGuess_Click(object sender, EventArgs e) { }
Přidejte následující kód, který zajistí, že je v seznamu pracovních postupů vybraný pracovní postup a zda je odhad uživatele platný.
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; }
Dále načtěte
WorkflowApplicationInstance
trvalou instanci pracovního postupu. AWorkflowApplicationInstance
představuje trvalou instanci pracovního postupu, která ještě nebyla přidružena k definici pracovního postupu. TheDefinitionIdentity
of theWorkflowApplicationInstance
contains theWorkflowIdentity
persisted workflow instance. V tomto kurzuWorkflowVersionMap
se třída nástroje používá k mapováníWorkflowIdentity
na správnou definici pracovního postupu. Jakmile se definice pracovního postupu načte, vytvoří seWorkflowApplication
pomocí správné definice pracovního postupu.Dim instance As WorkflowApplicationInstance = _ WorkflowApplication.GetInstance(WorkflowInstanceId, store) ' Use the persisted WorkflowIdentity to retrieve the correct workflow ' definition from the dictionary. Dim wf As Activity = _ WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity) ' Associate the WorkflowApplication with the correct definition Dim wfApp As New WorkflowApplication(wf, instance.DefinitionIdentity)
WorkflowApplicationInstance instance = WorkflowApplication.GetInstance(WorkflowInstanceId, store); // Use the persisted WorkflowIdentity to retrieve the correct workflow // definition from the dictionary. Activity wf = WorkflowVersionMap.GetWorkflowDefinition(instance.DefinitionIdentity); // Associate the WorkflowApplication with the correct definition var wfApp = new WorkflowApplication(wf, instance.DefinitionIdentity);
WorkflowApplication
Po vytvoření nakonfigurujte úložiště instancí, obslužné rutiny životního cyklu pracovního postupu a rozšíření volánímConfigureWorkflowApplication
. Tyto kroky musí být provedeny při každém vytvoření novéhoWorkflowApplication
a musí být provedeny před načtením instance pracovního postupu doWorkflowApplication
. Po načtení pracovního postupu se obnoví odhad uživatele.' 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);
Nakonec vymažte textové pole pro odhad a připravte formulář na přijetí dalšího odhadu.
' Clear the Guess textbox. Guess.Clear() Guess.Focus()
// Clear the Guess textbox. Guess.Clear(); Guess.Focus();
Následující příklad je dokončená obslužná
EnterGuess_Click
rutina.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(); }
Ukončení pracovního postupu
Přidání obslužné rutiny
Click
proQuitGame
. Chcete-li přidat obslužnou rutinu, přepněte do návrhového zobrazení formuláře a poklikejte naQuitGame
tlačítko . Kdykoli uživatel klikne na toto tlačítko, ukončí se aktuálně vybraný pracovní postup.Private Sub QuitGame_Click(sender As Object, e As EventArgs) Handles QuitGame.Click End Sub
private void QuitGame_Click(object sender, EventArgs e) { }
Do obslužné rutiny
QuitGame_Click
přidejte následující kód. Tento kód nejprve zkontroluje, jestli je v seznamu pracovních postupů vybraný pracovní postup. Pak načte trvalou instanci do ,WorkflowApplicationInstance
použijeDefinitionIdentity
k určení správné definice pracovního postupu a pak inicializujeWorkflowApplication
. Dále jsou obslužné rutiny životního cyklu rozšíření a pracovního postupu nakonfigurovány s volánímConfigureWorkflowApplication
. Po nakonfigurováníWorkflowApplication
se načte a pakTerminate
se zavolá.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.");
Sestavení a spuštění aplikace
Poklikáním na Program.cs (nebo Module1.vb) v Průzkumník řešení zobrazíte kód.
Do horní části souboru přidejte následující
using
příkaz (neboImports
) s jinýmiusing
příkazy (neboImports
).Imports System.Windows.Forms
using System.Windows.Forms;
Odeberte nebo zakomentujte existující kód hostování pracovního postupu z postupu: Spusťte pracovní postup a nahraďte ho následujícím kódem.
Sub Main() Application.EnableVisualStyles() Application.Run(New WorkflowHostForm()) End Sub
static void Main(string[] args) { Application.EnableVisualStyles(); Application.Run(new WorkflowHostForm()); }
Klikněte pravým tlačítkem na NumberGuessWorkflowHost v Průzkumník řešení a zvolte Vlastnosti. Na kartě Aplikace zadejte aplikaci systému Windows pro typ výstupu. Tento krok je nepovinný, ale pokud není následovaný oknem konzoly, zobrazí se vedle formuláře.
Stisknutím kombinace kláves Ctrl+Shift+B sestavte aplikaci.
Ujistěte se, že je parametr NumberGuessWorkflowHost nastavený jako spouštěcí aplikace, a stisknutím kombinace kláves Ctrl+F5 spusťte aplikaci.
Vyberte oblast pro hádanou hru a typ pracovního postupu, který chcete spustit, a klikněte na Tlačítko Nová hra. Do pole Hádejte zadejte odhad a kliknutím na Přejít odešlete svůj odhad. Všimněte si, že výstup z
WriteLine
aktivit se zobrazí ve formuláři.Spusťte několik pracovních postupů pomocí různých typů pracovních postupů a rozsahů čísel, zadejte určité odhady a přepněte mezi pracovními postupy výběrem ze seznamu ID instance pracovního postupu.
Všimněte si, že když přepnete na nový pracovní postup, předchozí odhady a průběh pracovního postupu se v okně stavu nezobrazí. Důvodem, proč stav není dostupný, je to, že se nezachytí a neuloží kamkoliv. V dalším kroku kurzu Postupy : Vytvoření vlastního účastníka sledování vytvoříte vlastního účastníka sledování, který uloží tyto informace.