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
Ö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.
Välj Öppna, Arkiv på Arkiv-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
Välj SqlWorkflowInstanceStoreSchema.sql på menyn Fönster . Kontrollera att WF45GettingStartedTutorial är markerat i listrutan Tillgängliga databaser och välj Kör på frågemenyn.
Välj SqlWorkflowInstanceStoreLogic.sql på menyn Fönster . Kontrollera att WF45GettingStartedTutorial är markerat i listrutan Tillgängliga databaser och välj Kör på 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
Högerklicka på NumberGuessWorkflowHost i Solution Explorer och välj Lägg till referens.
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.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
Högerklicka på NumberGuessWorkflowHost i Solution Explorer och välj Lägg till nytt objekt.
I listan Installerade mallar väljer du Windows-formulär, skriver
WorkflowHostForm
i rutan Namn och klickar på Lägg till.Konfigurera följande egenskaper i formuläret.
Property Värde FormBorderStyle FixedSingle MaximizeBox Falsk Storlek 400, 420 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 spelEtikett Plats: 94, 18
Text: Gissa ett tal från 1 tillKombinationsruta Namn: NumberRange
DropDownStyle: Listruta
Objekt: 10, 100, 1000
Plats: 228, 12
Storlek: 143, 21Etikett Plats: 13, 43
Text: ArbetsflödestypKombinationsruta Namn: WorkflowType
DropDownStyle: Listruta
Objekt: StateMachineNumberGuessWorkflow, FlowchartNumberGuessWorkflow, SekventielltNumberGuessWorkflow
Plats: 94, 40
Storlek: 277, 21Etikett Namn: WorkflowVersion
Plats: 13, 362
Text: ArbetsflödesversionGruppruta Plats: 13, 67
Storlek: 358, 287
Text: SpelKommentar
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-IDKombinationsruta Namn: InstanceId
DropDownStyle: Listruta
Plats: 121, 17
Storlek: 227, 21Etikett Plats: 7, 47
Text: GissaTextBox Namn: Gissa
Plats: 50, 44
Storlek: 65, 20Knapp Namn: EnterGuess
Plats: 121, 42
Storlek: 75, 23
Text: Ange GissaKnapp Namn: QuitGame
Plats: 274, 42
Storlek: 75, 23
Text: AvslutaTextBox Namn: WorkflowStatus
Plats: 10, 73
Flera linjer: Sant
ReadOnly: Sant
Rullningslister: Lodrätt
Storlek: 338, 208Ange formulärets AcceptButton-egenskap till EnterGuess.
I följande exempel visas det ifyllda formuläret.
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.
Högerklicka på WorkflowHostForm i Solution Explorer och välj Visa kod.
Lägg till följande
using
(ellerImports
) -instruktioner överst i filen med de andrausing
(ellerImports
) -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;
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.Lägg till en
WorkflowInstanceId
egenskap iWorkflowFormHost
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 ochWorkflowInstanceId
egenskapen returnerar det valda arbetsflödet.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) { }
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 iInstanceId
kombinationsrutan.Lägg till en
SelectedIndexChanged
hanterare förInstanceId
. Om du vill lägga till hanteraren växlar du till Designvy för formuläret, markerarInstanceId
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) { }
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(); }
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 icboInstanceId
kombinationsrutan.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(); } }
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
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.I
ConfigureWorkflowApplication
anger duSqlWorkflowInstanceStore
förWorkflowApplication
.' Configure the persistence store. wfApp.InstanceStore = store
// Configure the persistence store. wfApp.InstanceStore = store;
Skapa sedan en
StringWriter
instans och lägg till den iExtensions
samlingen avWorkflowApplication
. När enStringWriter
läggs till i tilläggen avbildas allaWriteLine
aktivitetsutdata. När arbetsflödet blir inaktivtWriteLine
kan utdata extraheras frånStringWriter
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);
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 hanterarenGameOver
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(); };
Lägg till följande
Aborted
ochOnUnhandledException
hanterare. MetodenGameOver
anropas inte frånAborted
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; };
Lägg till följande
PersistableIdle
hanterare. Den här hanteraren hämtar tilläggetStringWriter
som lades till, extraherar utdata från aktiviteternaWriteLine
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.
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.Lägg till följande
using
ellerImports
-instruktioner överst i filen med de andrausing
ellerImports
-uttrycken.Imports System.Activities Imports NumberGuessWorkflowActivities
using System.Activities; using NumberGuessWorkflowActivities;
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
Lägg till en
Click
hanterare förNewGame
. Om du vill lägga till hanteraren växlar du till Designvy för formuläret och dubbelklickar påNewGame
. EnNewGame_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) { }
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));
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älpklassenWorkflowVersionMap
. Därefter skapas en nyWorkflowApplication
instans med hjälp av arbetsflödesdefinitionen,WorkflowIdentity
och 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);
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;
Anropa
ConfigureWorkflowApplication
för att konfigurera instansarkivet, tilläggen och arbetsflödets livscykelhanterare för den härWorkflowApplication
instansen.' Configure the instance store, extensions, and ' workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp)
// Configure the instance store, extensions, and // workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp);
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
Lägg till en
Click
hanterare förEnterGuess
. 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) { }
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; }
Hämta sedan den
WorkflowApplicationInstance
bevarade arbetsflödesinstansen. AWorkflowApplicationInstance
representerar en instans av ett beständiga arbetsflöde som ännu inte har associerats med en arbetsflödesdefinition. IDefinitionIdentity
innehållerWorkflowApplicationInstance
den bevarade arbetsflödesinstansenWorkflowIdentity
. I den här självstudien används verktygsklassenWorkflowVersionMap
för att mappaWorkflowIdentity
till rätt arbetsflödesdefinition. När arbetsflödesdefinitionen har hämtats skapas enWorkflowApplication
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);
När har
WorkflowApplication
skapats konfigurerar du instansarkivet, arbetsflödets livscykelhanterare och tillägg genom att anropaConfigureWorkflowApplication
. De här stegen måste utföras varje gång en nyWorkflowApplication
skapas och de måste göras innan arbetsflödesinstansenWorkflowApplication
lä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);
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
Lägg till en
Click
hanterare förQuitGame
. 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) { }
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 instansenDefinitionIdentity
WorkflowApplication
in i ,WorkflowApplicationInstance
anvä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 tillConfigureWorkflowApplication
. När harWorkflowApplication
konfigurerats läses den in och anropas sedanTerminate
.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
Dubbelklicka på Program.cs (eller Module1.vb) i Solution Explorer för att visa koden.
Lägg till följande
using
(ellerImports
) -instruktion överst i filen med de andrausing
(ellerImports
) -uttrycken.Imports System.Windows.Forms
using System.Windows.Forms;
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()); }
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.
Tryck på Ctrl+Skift+B för att skapa programmet.
Kontrollera att NumberGuessWorkflowHost har angetts som startprogram och tryck på Ctrl+F5 för att starta programmet.
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å Gå för att skicka din gissning. Observera att utdata från aktiviteterna
WriteLine
visas i formuläret.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.