Como criar e executar um fluxo de trabalho de longa duração
Um dos recursos centrais do Windows Workflow Foundation (WF) é a capacidade do tempo de execução de persistir e descarregar fluxos de trabalho ociosos em um banco de dados. As etapas em Como: Executar um fluxo de trabalho demonstraram os conceitos básicos de hospedagem de fluxo de trabalho usando um aplicativo de console. Foram mostrados exemplos de fluxos de trabalho iniciais, manipuladores de ciclo de vida do fluxo de trabalho e marcadores de retomada. Para demonstrar a persistência do fluxo de trabalho de forma eficaz, é necessário um host de fluxo de trabalho mais complexo que suporte a inicialização e retomada de várias instâncias de fluxo de trabalho. Esta etapa do tutorial demonstra como criar um aplicativo host de formulário do Windows que oferece suporte a iniciar e retomar várias instâncias de fluxo de trabalho, persistência de fluxo de trabalho e fornece uma base para os recursos avançados, como controle e controle de versão, demonstrados nas etapas subsequentes do tutorial.
Nota
Esta etapa do tutorial e as etapas subsequentes usam todos os três tipos de fluxo de trabalho de Como: Criar um fluxo de trabalho.
Para criar o banco de dados de persistência
Abra o SQL Server Management Studio e conecte-se ao servidor local, por exemplo, .\SQLEXPRESS. Clique com o botão direito do mouse no nó Bancos de dados no servidor local e selecione Novo banco de dados. Nomeie o novo banco de dados WF45GettingStartedTutorial, aceite todos os outros valores e selecione OK.
Nota
Verifique se você tem a permissão Criar Banco de Dados no servidor local antes de criar o banco de dados.
Escolha Abrir, Arquivo no menu Arquivo. Navegue até a seguinte pasta: C:\Windows\Microsoft.NET\Framework\v4.0.30319\sql\en
Selecione os dois arquivos a seguir e clique em Abrir.
SqlWorkflowInstanceStoreLogic.sql
SqlWorkflowInstanceStoreSchema.sql
Escolha SqlWorkflowInstanceStoreSchema.sql no menu Janela . Verifique se WF45GettingStartedTutorial está selecionado na lista suspensa Bancos de dados disponíveis e escolha Executar no menu Consulta.
Escolha SqlWorkflowInstanceStoreLogic.sql no menu Janela . Verifique se WF45GettingStartedTutorial está selecionado na lista suspensa Bancos de dados disponíveis e escolha Executar no menu Consulta.
Aviso
É importante executar as duas etapas anteriores na ordem correta. Se as consultas forem executadas fora de ordem, ocorrerão erros e o banco de dados de persistência não está configurado corretamente.
Para adicionar a referência aos assemblies DurableInstancing
Clique com o botão direito do mouse em NumberGuessWorkflowHost no Gerenciador de Soluções e selecione Adicionar Referência.
Selecione Assemblies na lista Add Reference e digite
DurableInstancing
na caixa Search Assemblies . Isso filtra as montagens e torna as referências desejadas mais fáceis de selecionar.Marque a caixa de seleção ao lado de System.Activities.DurableInstancing e System.Runtime.DurableInstancing na lista Resultados da Pesquisa e clique em OK.
Para criar o formulário de host de fluxo de trabalho
Clique com o botão direito do mouse em NumberGuessWorkflowHost no Gerenciador de Soluções e escolha Adicionar, Novo Item.
Na lista Modelos instalados, escolha Windows Form, digite
WorkflowHostForm
na caixa Nome e clique em Adicionar.Configure as seguintes propriedades no formulário.
Property valor FormBorderStyle FixoÚnico MaximizeBox False Tamanho 400, 420 Adicione os seguintes controles ao formulário na ordem especificada e configure as propriedades conforme indicado.
Controlo Propriedade: Valor Botão Designação: NewGame
Localização: 13, 13
Tamanho: 75, 23
Texto: Novo JogoEtiqueta Localização: 94, 18
Texto: Adivinhe um número de 1 aCaixa de Combinação Designação: NumberRange
DropDownStyle: DropDownList
Itens: 10, 100, 1000
Localização: 228, 12
Tamanho: 143, 21Etiqueta Localização: 13, 43
Texto: Tipo de fluxo de trabalhoCaixa de Combinação Nome: WorkflowType
DropDownStyle: DropDownList
Itens: StateMachineNumberGuessWorkflow, FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflow
Localização: 94, 40
Tamanho: 277, 21Etiqueta Nome: WorkflowVersion
Localização: 13, 362
Texto: Versão do fluxo de trabalhoCaixa de Grupo Localização: 13, 67
Tamanho: 358, 287
Texto: JogoNota
Ao adicionar os seguintes controles, coloque-os no GroupBox.
Controlo Propriedade: Valor Etiqueta Localização: 7, 20
Texto: ID da instância do fluxo de trabalhoCaixa de Combinação Nome: InstanceId
DropDownStyle: DropDownList
Localização: 121, 17
Tamanho: 227, 21Etiqueta Localização: 7, 47
Texto: GuessTextBox Designação: Guess
Localização: 50, 44
Tamanho: 65, 20Botão Designação: EnterGuess
Localização: 121, 42
Tamanho: 75, 23
Texto: Digite GuessBotão Designação: QuitGame
Localização: 274, 42
Tamanho: 75, 23
Texto: SairTextBox Nome: WorkflowStatus
Localização: 10, 73
Multilinha: Verdadeiro
Somente leitura: Verdadeiro
Barras de rolagem: Vertical
Tamanho: 338, 208Defina a propriedade AcceptButton do formulário como EnterGuess.
O exemplo a seguir ilustra o formulário preenchido.
Para adicionar as propriedades e os métodos auxiliares do formulário
As etapas nesta seção adicionam propriedades e métodos auxiliares à classe de formulário que configuram a interface do usuário do formulário para dar suporte à execução e retomada de fluxos de trabalho de adivinhação de número.
Clique com o botão direito do mouse em WorkflowHostForm no Gerenciador de Soluções e escolha Exibir Código.
Adicione as seguintes
using
(ouImports
) instruções na parte superior do arquivo com as outrasusing
(ouImports
) instruções.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;
Adicione as seguintes declarações de membro à classe WorkflowHostForm .
Importante
A Microsoft recomenda que você use o fluxo de autenticação mais seguro disponível. Se você estiver se conectando ao SQL do Azure, as Identidades Gerenciadas para recursos do Azure serão o método de autenticação recomendado.
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;
Nota
Se a cadeia de conexão for diferente, atualize
connectionString
para fazer referência ao banco de dados.Adicione uma
WorkflowInstanceId
propriedade àWorkflowFormHost
classe.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; } }
A
InstanceId
caixa de combinação exibe uma lista de ids de instância de fluxo de trabalho persistente e aWorkflowInstanceId
propriedade retorna o fluxo de trabalho selecionado no momento.Adicione um manipulador para o evento de formulário
Load
. Para adicionar o manipulador, alterne para o modo Design do formulário, clique no ícone Eventos na parte superior da janela Propriedades e clique duas vezes em Carregar.Private Sub WorkflowHostForm_Load(sender As Object, e As EventArgs) Handles Me.Load End Sub
private void WorkflowHostForm_Load(object sender, EventArgs e) { }
Adicione o seguinte código a
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();
Quando o formulário é carregado, o
SqlWorkflowInstanceStore
é configurado, as caixas de combinação intervalo e tipo de fluxo de trabalho são definidas como valores padrão e as instâncias de fluxo de trabalho persistentes são adicionadas àInstanceId
caixa de combinação.Adicione um
SelectedIndexChanged
manipulador paraInstanceId
. Para adicionar o manipulador, alterne para o modo Design do formulário, selecione aInstanceId
caixa de combinação, clique no ícone Eventos na parte superior da janela Propriedades e clique duas vezes em SelectedIndexChanged.Private Sub InstanceId_SelectedIndexChanged(sender As Object, e As EventArgs) Handles InstanceId.SelectedIndexChanged End Sub
private void InstanceId_SelectedIndexChanged(object sender, EventArgs e) { }
Adicione o seguinte código a
InstanceId_SelectedIndexChanged
. Sempre que o usuário seleciona um fluxo de trabalho usando a caixa de combinação, esse manipulador atualiza a janela de status.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(); }
Adicione o seguinte
ListPersistedWorkflows
método à classe de formulário.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
Consulta o armazenamento de instâncias para instâncias de fluxo de trabalho persistentes e adiciona as IDs de instância àcboInstanceId
caixa de combinação.Adicione o seguinte
UpdateStatus
método e o delegado correspondente à classe de formulário. Esse método atualiza a janela de status no formulário com o status do fluxo de trabalho em execução no momento.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(); } }
Adicione o seguinte
GameOver
método e o delegado correspondente à classe de formulário. Quando um fluxo de trabalho é concluído, esse método atualiza a interface do usuário do formulário removendo a ID da instância do fluxo de trabalho concluído da caixa de combinação 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; } }
Para configurar o armazenamento de instâncias, manipuladores de ciclo de vida do fluxo de trabalho e extensões
Adicione um
ConfigureWorkflowApplication
método à classe de formulário.Private Sub ConfigureWorkflowApplication(wfApp As WorkflowApplication) End Sub
private void ConfigureWorkflowApplication(WorkflowApplication wfApp) { }
Esse método configura o
WorkflowApplication
, adiciona as extensões desejadas e adiciona manipuladores para os eventos do ciclo de vida do fluxo de trabalho.Em
ConfigureWorkflowApplication
, especifique oSqlWorkflowInstanceStore
para oWorkflowApplication
.' Configure the persistence store. wfApp.InstanceStore = store
// Configure the persistence store. wfApp.InstanceStore = store;
Em seguida, crie uma
StringWriter
instância e adicione-aExtensions
à coleção doWorkflowApplication
. Quando umStringWriter
é adicionado às extensões, ele captura todaWriteLine
a saída da atividade. Quando o fluxo de trabalho fica ocioso, aWriteLine
saída pode ser extraída doStringWriter
formulário e exibida no formulário.' 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);
Adicione o seguinte manipulador para o
Completed
evento. Quando um fluxo de trabalho é concluído com êxito, o número de voltas realizadas para adivinhar o número é exibido na janela de status. Se o fluxo de trabalho for encerrado, as informações de exceção que causaram o encerramento serão exibidas. No final do manipulador, oGameOver
método é chamado, o que remove o fluxo de trabalho concluído da lista de fluxos de trabalho.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(); };
Adicione o seguinte
Aborted
eOnUnhandledException
manipuladores. OGameOver
método não é chamado doAborted
manipulador porque quando uma instância de fluxo de trabalho é anulada, ela não termina e é possível retomar a instância posteriormente.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; };
Adicione o seguinte
PersistableIdle
manipulador. Esse manipulador recupera aStringWriter
extensão que foi adicionada, extrai a saída das atividades e a exibe na janela deWriteLine
status.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; };
A PersistableIdleAction enumeração tem três valores: None, Persist, e Unload. Persist faz com que o fluxo de trabalho persista, mas não faz com que o fluxo de trabalho seja descarregado. Unload faz com que o fluxo de trabalho persista e seja descarregado.
O exemplo a seguir é o método complete
ConfigureWorkflowApplication
.Private Sub ConfigureWorkflowApplication(wfApp As WorkflowApplication) ' Configure the persistence store. wfApp.InstanceStore = store ' Add a StringWriter to the extensions. This captures the output ' from the WriteLine activities so we can display it in the form. Dim sw As New StringWriter() wfApp.Extensions.Add(sw) 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; }; }
Para habilitar o início e a retomada de vários tipos de fluxo de trabalho
Para retomar uma instância de fluxo de trabalho, o host precisa fornecer a definição de fluxo de trabalho. Neste tutorial, há três tipos de fluxo de trabalho, e as etapas subsequentes do tutorial apresentam várias versões desses tipos. WorkflowIdentity
Fornece uma maneira para um aplicativo host associar informações de identificação a uma instância de fluxo de trabalho persistente. As etapas nesta seção demonstram como criar uma classe de utilitário para ajudar a mapear a identidade do fluxo de trabalho de uma instância de fluxo de trabalho persistente para a definição de fluxo de trabalho correspondente. Para obter mais informações sobre WorkflowIdentity
controle de versão e controle de versão, consulte Usando WorkflowIdentity e controle de versão.
Clique com o botão direito do mouse em NumberGuessWorkflowHost no Gerenciador de Soluções e escolha Adicionar, Classe. Digite
WorkflowVersionMap
na caixa Nome e clique em Adicionar.Adicione as instruções a seguir
using
ouImports
na parte superior do arquivo com as outrasusing
Imports
ou instruções.Imports System.Activities Imports NumberGuessWorkflowActivities
using System.Activities; using NumberGuessWorkflowActivities;
Substitua a declaração de
WorkflowVersionMap
classe pela seguinte declaração.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
Contém três identidades de fluxo de trabalho que mapeiam para as três definições de fluxo de trabalho deste tutorial e é usado nas seções a seguir quando os fluxos de trabalho são iniciados e retomados.
Para iniciar um novo fluxo de trabalho
Adicione um
Click
manipulador paraNewGame
. Para adicionar o manipulador, alterne para o modo Design do formulário e clique duas vezes emNewGame
. UmNewGame_Click
manipulador é adicionado e o modo de exibição alterna para o modo de exibição de código para o formulário. Sempre que o usuário clica nesse botão, um novo fluxo de trabalho é iniciado.Private Sub NewGame_Click(sender As Object, e As EventArgs) Handles NewGame.Click End Sub
private void NewGame_Click(object sender, EventArgs e) { }
Adicione o seguinte código ao manipulador de cliques. Esse código cria um dicionário de argumentos de entrada para o fluxo de trabalho, digitado pelo nome do argumento. Este dicionário tem uma entrada que contém o intervalo do número gerado aleatoriamente recuperado da caixa de combinação de intervalo.
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));
Em seguida, adicione o seguinte código que inicia o fluxo de trabalho. A
WorkflowIdentity
definição de fluxo de trabalho correspondente ao tipo de fluxo de trabalho selecionado é recuperada usando aWorkflowVersionMap
classe auxiliar. Em seguida, uma novaWorkflowApplication
instância é criada usando a definiçãoWorkflowIdentity
de fluxo de trabalho e o dicionário de argumentos de entrada.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);
Em seguida, adicione o código a seguir que adiciona o fluxo de trabalho à lista de fluxos de trabalho e exibe as informações de versão do fluxo de trabalho no formulário.
' 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;
Chamada
ConfigureWorkflowApplication
para configurar o armazenamento de instâncias, extensões e manipuladores de ciclo de vida do fluxo de trabalho para estaWorkflowApplication
instância.' Configure the instance store, extensions, and ' workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp)
// Configure the instance store, extensions, and // workflow lifecycle handlers. ConfigureWorkflowApplication(wfApp);
Por fim, ligue para
Run
.' Start the workflow. wfApp.Run()
// Start the workflow. wfApp.Run();
O exemplo a seguir é o manipulador concluído
NewGame_Click
.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(); }
Para retomar um fluxo de trabalho
Adicione um
Click
manipulador paraEnterGuess
. Para adicionar o manipulador, alterne para o modo Design do formulário e clique duas vezes emEnterGuess
. Sempre que o usuário clica nesse botão, um fluxo de trabalho é retomado.Private Sub EnterGuess_Click(sender As Object, e As EventArgs) Handles EnterGuess.Click End Sub
private void EnterGuess_Click(object sender, EventArgs e) { }
Adicione o código a seguir para garantir que um fluxo de trabalho seja selecionado na lista de fluxo de trabalho e que a suposição do usuário seja válida.
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; }
Em seguida, recupere a
WorkflowApplicationInstance
instância do fluxo de trabalho persistente. AWorkflowApplicationInstance
representa uma instância de fluxo de trabalho persistente que ainda não foi associada a uma definição de fluxo de trabalho. ODefinitionIdentity
deWorkflowApplicationInstance
contém oWorkflowIdentity
da instância de fluxo de trabalho persistente. Neste tutorial, aWorkflowVersionMap
classe de utilitário é usada para mapear aWorkflowIdentity
definição de fluxo de trabalho correta. Depois que a definição do fluxo de trabalho é recuperada, umWorkflowApplication
é criado, usando a definição de fluxo de trabalho correta.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);
Depois de
WorkflowApplication
criado, configure o armazenamento de instâncias, manipuladores de ciclo de vida do fluxo de trabalho e extensões chamandoConfigureWorkflowApplication
. Essas etapas devem ser feitas sempre que um novoWorkflowApplication
é criado e devem ser feitas antes que a instância do fluxo de trabalho seja carregada noWorkflowApplication
. Depois que o fluxo de trabalho é carregado, ele é retomado com o palpite do usuário.' 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);
Finalmente, desmarque a caixa de texto de adivinhação e prepare o formulário para aceitar outra suposição.
' Clear the Guess textbox. Guess.Clear() Guess.Focus()
// Clear the Guess textbox. Guess.Clear(); Guess.Focus();
O exemplo a seguir é o manipulador concluído
EnterGuess_Click
.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(); }
Para encerrar um fluxo de trabalho
Adicione um
Click
manipulador paraQuitGame
. Para adicionar o manipulador, alterne para o modo Design do formulário e clique duas vezes emQuitGame
. Sempre que o usuário clica nesse botão, o fluxo de trabalho selecionado no momento é encerrado.Private Sub QuitGame_Click(sender As Object, e As EventArgs) Handles QuitGame.Click End Sub
private void QuitGame_Click(object sender, EventArgs e) { }
Adicione o seguinte código ao
QuitGame_Click
manipulador. Esse código primeiro verifica se um fluxo de trabalho está selecionado na lista de fluxos de trabalho. Em seguida, ele carrega a instância persistente em umWorkflowApplicationInstance
, usa oDefinitionIdentity
para determinar a definição de fluxo de trabalho correta e, em seguida, inicializa oWorkflowApplication
. Em seguida, as extensões e os manipuladores do ciclo de vida do fluxo de trabalho são configurados com uma chamada paraConfigureWorkflowApplication
.WorkflowApplication
Uma vez configurado, ele é carregado e, em seguidaTerminate
, é chamado.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.");
Para criar e executar o aplicativo
Clique duas vezes em Program.cs (ou Module1.vb) no Gerenciador de Soluções para exibir o código.
Adicione a seguinte
using
(ouImports
) instrução na parte superior do arquivo com as outrasusing
(ouImports
) instruções.Imports System.Windows.Forms
using System.Windows.Forms;
Remova ou comente o código de hospedagem de fluxo de trabalho existente de Como: Executar um fluxo de trabalho e substitua-o pelo código a seguir.
Sub Main() Application.EnableVisualStyles() Application.Run(New WorkflowHostForm()) End Sub
static void Main(string[] args) { Application.EnableVisualStyles(); Application.Run(new WorkflowHostForm()); }
Clique com o botão direito do mouse em NumberGuessWorkflowHost no Gerenciador de Soluções e escolha Propriedades. Na guia Aplicativo, especifique Aplicativo do Windows para o tipo de saída. Esta etapa é opcional, mas se não for seguida, a janela do console será exibida além do formulário.
Pressione Ctrl+Shift+B para criar o aplicativo.
Verifique se NumberGuessWorkflowHost está definido como o aplicativo de inicialização e pressione Ctrl+F5 para iniciar o aplicativo.
Selecione um intervalo para o jogo de adivinhação e o tipo de fluxo de trabalho a iniciar e clique em Novo Jogo. Insira um palpite na caixa Adivinhar e clique em Ir para enviar seu palpite. Observe que a
WriteLine
saída das atividades é exibida no formulário.Inicie vários fluxos de trabalho usando diferentes tipos de fluxo de trabalho e intervalos de números, insira algumas suposições e alterne entre os fluxos de trabalho selecionando na lista ID da instância do fluxo de trabalho.
Observe que quando você alterna para um novo fluxo de trabalho, as suposições anteriores e o progresso do fluxo de trabalho não são exibidos na janela de status. A razão pela qual o status não está disponível é porque ele não é capturado e salvo em qualquer lugar. Na próxima etapa do tutorial, Como: Criar um participante de acompanhamento personalizado, você cria um participante de acompanhamento personalizado que salva essas informações.