Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O Windows Workflow Foundation (WF) fornece vários métodos de hospedagem de fluxos de trabalho. WorkflowInvoker fornece uma maneira simples de invocar um fluxo de trabalho como se fosse uma chamada de método e pode ser usado apenas para fluxos de trabalho que não usam persistência. WorkflowApplication fornece um modelo mais avançado para executar fluxos de trabalho que inclui notificação de eventos do ciclo de vida, controle de execução, retomada de marcações e persistência. WorkflowServiceHost fornece suporte para atividades de mensagens e é usado principalmente com serviços de fluxo de trabalho. Este tópico apresenta a hospedagem de fluxo de trabalho com WorkflowInvoker e WorkflowApplication. Para obter mais informações sobre como hospedar fluxos de trabalho com WorkflowServiceHost, consulte Workflow Services e Hosting Workflow Services Overview.
Usando WorkflowInvoker
WorkflowInvoker fornece um modelo para executar um fluxo de trabalho como se fosse uma chamada de método. Para invocar um fluxo de trabalho usando WorkflowInvoker, chame o Invoke método e passe a definição de fluxo de trabalho do fluxo de trabalho a ser invocado. Neste exemplo, uma WriteLine atividade é invocada usando WorkflowInvoker.
Activity wf = new WriteLine
{
Text = "Hello World."
};
WorkflowInvoker.Invoke(wf);
Quando um fluxo de trabalho é invocado usando WorkflowInvoker, o fluxo de trabalho é executado no thread de chamada e o Invoke método é bloqueado até que o fluxo de trabalho seja concluído, incluindo qualquer tempo ocioso. Para configurar um intervalo de tempo limite em que o fluxo de trabalho deveria concluir, use uma das sobrecargas de Invoke que usa um parâmetro de TimeSpan . Neste exemplo, um fluxo de trabalho é invocado duas vezes com dois intervalos de tempo limite diferentes. O primeiro fluxo de trabalho é concluído, mas o segundo não.
Activity wf = new Sequence()
{
Activities =
{
new WriteLine()
{
Text = "Before the 1 minute delay."
},
new Delay()
{
Duration = TimeSpan.FromMinutes(1)
},
new WriteLine()
{
Text = "After the 1 minute delay."
}
}
};
// This workflow completes successfully.
WorkflowInvoker.Invoke(wf, TimeSpan.FromMinutes(2));
// This workflow does not complete and a TimeoutException
// is thrown.
try
{
WorkflowInvoker.Invoke(wf, TimeSpan.FromSeconds(30));
}
catch (TimeoutException ex)
{
Console.WriteLine(ex.Message);
}
Observação
TimeoutException é acionada somente se o intervalo de tempo limite decorre e fluxo de trabalho se torna ocioso durante a execução. Um fluxo de trabalho que recebe mais tempo do intervalo de tempo limite especificado para concluir concluída com êxito se o fluxo de trabalho não se torna ocioso.
WorkflowInvoker também fornece versões assíncronas do método invoke. Para obter mais informações, consulte InvokeAsync e BeginInvoke.
Configurando argumentos de entrada de um fluxo de trabalho
Os dados podem ser passados em um fluxo de trabalho usando um dicionário de parâmetros de entrada, fechado pelo nome de argumento, que mapeiam para argumentos de entrada de fluxo de trabalho. Neste exemplo, um WriteLine é invocado e o valor de seu Text argumento é especificado usando o dicionário de parâmetros de entrada.
Activity wf = new WriteLine();
Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World.");
WorkflowInvoker.Invoke(wf, inputs);
Recuperando argumentos de saída de um fluxo de trabalho
Os parâmetros de saída de um fluxo de trabalho podem ser obtidos por meio do dicionário de resultados que é retornado pela chamada para Invoke. O exemplo a seguir invoca um fluxo de trabalho que consiste em uma única Divide atividade que tem dois argumentos de entrada e dois argumentos de saída. Quando o fluxo de trabalho é invocado, o arguments dicionário é passado, que contém os valores de cada argumento de entrada, chaveado pelo nome do argumento. Quando a chamada para Invoke retorna, cada argumento de saída é devolvido no dicionário outputs, também indexado pelo nome do argumento.
public sealed class Divide : CodeActivity
{
[RequiredArgument]
public InArgument<int> Dividend { get; set; }
[RequiredArgument]
public InArgument<int> Divisor { get; set; }
public OutArgument<int> Remainder { get; set; }
public OutArgument<int> Result { get; set; }
protected override void Execute(CodeActivityContext context)
{
int quotient = Dividend.Get(context) / Divisor.Get(context);
int remainder = Dividend.Get(context) % Divisor.Get(context);
Result.Set(context, quotient);
Remainder.Set(context, remainder);
}
}
int dividend = 500;
int divisor = 36;
Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);
IDictionary<string, object> outputs =
WorkflowInvoker.Invoke(new Divide(), arguments);
Console.WriteLine($"{dividend} / {divisor} = {outputs["Result"]} Remainder {outputs["Remainder"]}");
Se o fluxo de trabalho derivar de ActivityWithResult, como CodeActivity<TResult> ou Activity<TResult>, e houver argumentos de saída além do argumento de saída bem definido Result , uma sobrecarga Invoke não genérica deve ser usada para recuperar os argumentos adicionais. Para fazer isso, a definição de fluxo de trabalho passada Invoke deve ser do tipo Activity. Neste exemplo, a Divide atividade deriva de CodeActivity<int>, mas é declarada como Activity para que uma sobrecarga não genérica de Invoke seja usada, que retorna um dicionário de argumentos em vez de um único valor retornado.
public sealed class Divide : CodeActivity<int>
{
public InArgument<int> Dividend { get; set; }
public InArgument<int> Divisor { get; set; }
public OutArgument<int> Remainder { get; set; }
protected override int Execute(CodeActivityContext context)
{
int quotient = Dividend.Get(context) / Divisor.Get(context);
int remainder = Dividend.Get(context) % Divisor.Get(context);
Remainder.Set(context, remainder);
return quotient;
}
}
int dividend = 500;
int divisor = 36;
Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);
Activity wf = new Divide();
IDictionary<string, object> outputs =
WorkflowInvoker.Invoke(wf, arguments);
Console.WriteLine($"{dividend} / {divisor} = {outputs["Result"]} Remainder {outputs["Remainder"]}");
Usando WorkflowApplication
WorkflowApplication fornece um conjunto avançado de recursos para o gerenciamento de instâncias de fluxo de trabalho. WorkflowApplication funciona como um proxy thread-safe para a WorkflowInstance real, que encapsula o runtime, e fornece métodos para criar e carregar instâncias de fluxo de trabalho, pausá-las, retomá-las e encerrá-las, além da notificação de eventos do ciclo de vida. Para executar um fluxo de trabalho usando WorkflowApplication, você cria o WorkflowApplication, inscreve-se em qualquer evento de ciclo de vida desejado, inicia o fluxo de trabalho e aguarda sua conclusão. Neste exemplo, uma definição de fluxo de trabalho que consiste em uma WriteLine atividade é criada e WorkflowApplication criada usando a definição de fluxo de trabalho especificada. Completed é tratado para que o host seja notificado quando o fluxo de trabalho for concluído, o fluxo de trabalho é iniciado com uma chamada a Run, e em seguida, o host aguarda a conclusão do fluxo de trabalho. Quando o fluxo de trabalho for concluído, AutoResetEvent será definido e o aplicativo hospedeiro poderá retomar a execução, conforme mostrado no exemplo a seguir.
AutoResetEvent syncEvent = new AutoResetEvent(false);
Activity wf = new WriteLine
{
Text = "Hello World."
};
// Create the WorkflowApplication using the desired
// workflow definition.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Handle the desired lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
syncEvent.Set();
};
// Start the workflow.
wfApp.Run();
// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();
Eventos do Ciclo de Vida da WorkflowApplication
Além de Completed, os autores de host podem ser notificados quando um fluxo de trabalho é descarregado (Unloaded), anuladas (Aborted), se torna ocioso (Idle e PersistableIdle), ou uma exceção não manipulada ocorre (OnUnhandledException). Os desenvolvedores de aplicativos de fluxo de trabalho podem lidar com essas notificações e tomar as medidas apropriadas, conforme mostrado no exemplo a seguir.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
}
else
{
Console.WriteLine($"Workflow {e.InstanceId} Completed.");
// Outputs can be retrieved from the Outputs dictionary,
// keyed by argument name.
// Console.WriteLine($"The winner is {e.Outputs["Winner"]}.");
}
};
wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
{
// Display the exception that caused the workflow
// to abort.
Console.WriteLine($"Workflow {e.InstanceId} Aborted.");
Console.WriteLine($"Exception: {e.Reason.GetType().FullName}\n{e.Reason.Message}");
};
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
// Perform any processing that should occur
// when a workflow goes idle. If the workflow can persist,
// both Idle and PersistableIdle are called in that order.
Console.WriteLine($"Workflow {e.InstanceId} Idle.");
};
wfApp.PersistableIdle = delegate (WorkflowApplicationIdleEventArgs e)
{
// Instruct the runtime to persist and unload the workflow.
// Choices are None, Persist, and Unload.
return PersistableIdleAction.Unload;
};
wfApp.Unloaded = delegate (WorkflowApplicationEventArgs e)
{
Console.WriteLine($"Workflow {e.InstanceId} Unloaded.");
};
wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
{
// Display the unhandled exception.
Console.WriteLine($"OnUnhandledException in Workflow {e.InstanceId}\n{e.UnhandledException.Message}");
Console.WriteLine($"ExceptionSource: {e.ExceptionSource.DisplayName} - {e.ExceptionSourceInstanceId}");
// Instruct the runtime to terminate the workflow.
// Other choices are Abort and Cancel. Terminate
// is the default if no OnUnhandledException handler
// is present.
return UnhandledExceptionAction.Terminate;
};
Configurando argumentos de entrada de um fluxo de trabalho
Os dados podem ser passados para um fluxo de trabalho assim que ele é iniciado usando um dicionário de parâmetros, semelhante à maneira como os dados são passados ao usar WorkflowInvoker. Cada item no dicionário é mapeado para um argumento de entrada do fluxo de trabalho especificado. Neste exemplo, um fluxo de trabalho que consiste em uma WriteLine atividade é invocado e seu Text argumento é especificado usando o dicionário de parâmetros de entrada.
AutoResetEvent syncEvent = new AutoResetEvent(false);
Activity wf = new WriteLine();
// Create the dictionary of input parameters.
Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World!");
// Create the WorkflowApplication using the desired
// workflow definition and dictionary of input parameters.
WorkflowApplication wfApp = new WorkflowApplication(wf, inputs);
// Handle the desired lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
syncEvent.Set();
};
// Start the workflow.
wfApp.Run();
// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();
Recuperando argumentos de saída de um fluxo de trabalho
Quando um fluxo de trabalho é concluído, todos os argumentos de saída podem ser recuperados no Completed manipulador acessando o WorkflowApplicationCompletedEventArgs.Outputs dicionário. O exemplo a seguir hospeda um fluxo de trabalho usando WorkflowApplication. Uma WorkflowApplication instância é construída usando uma definição de fluxo de trabalho que consiste em uma única DiceRoll atividade. A atividade de DiceRoll tem dois argumentos de saída que representam os resultados da operação de rolagem de dados. Quando o fluxo de trabalho é concluído, as saídas são recuperadas no Completed manipulador.
public sealed class DiceRoll : CodeActivity
{
public OutArgument<int> D1 { get; set; }
public OutArgument<int> D2 { get; set; }
static Random r = new Random();
protected override void Execute(CodeActivityContext context)
{
D1.Set(context, r.Next(1, 7));
D2.Set(context, r.Next(1, 7));
}
}
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(new DiceRoll());
// Subscribe to any desired workflow lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
}
else
{
Console.WriteLine($"Workflow {e.InstanceId} Completed.");
// Outputs can be retrieved from the Outputs dictionary,
// keyed by argument name.
Console.WriteLine($"The two dice are {e.Outputs["D1"]} and {e.Outputs["D2"]}.");
}
};
// Run the workflow.
wfApp.Run();
Observação
WorkflowApplication e WorkflowInvoker recebem um dicionário de argumentos de entrada e retornam um dicionário de argumentos out. Esses parâmetros de dicionário, propriedades e valores retornados são do tipo IDictionary<string, object>. A instância real da classe de dicionário que é passada pode ser qualquer classe que implemente IDictionary<string, object>. Nestes exemplos, Dictionary<string, object> é usado. Para obter mais informações sobre dicionários, consulte IDictionary<TKey,TValue> e Dictionary<TKey,TValue>.
Passando dados para um fluxo de trabalho em execução usando marcadores
Marcadores são o mecanismo pelo qual uma atividade pode esperar passivamente para ser retomada e são um mecanismo para passar dados para uma instância de fluxo de trabalho em execução. Se uma atividade estiver aguardando dados, ela poderá criar um Bookmark e registrar um método de retorno de chamada a ser chamado quando Bookmark for retomado, conforme mostrado no exemplo a seguir.
public sealed class ReadLine : NativeActivity<string>
{
[RequiredArgument]
public InArgument<string> BookmarkName { get; set; }
protected override void Execute(NativeActivityContext context)
{
// Create a Bookmark and wait for it to be resumed.
context.CreateBookmark(BookmarkName.Get(context),
new BookmarkCallback(OnResumeBookmark));
}
// NativeActivity derived activities that do asynchronous operations by calling
// one of the CreateBookmark overloads defined on System.Activities.NativeActivityContext
// must override the CanInduceIdle property and return true.
protected override bool CanInduceIdle
{
get { return true; }
}
public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj)
{
// When the Bookmark is resumed, assign its value to
// the Result argument.
Result.Set(context, (string)obj);
}
Quando executada, a atividade de ReadLine cria Bookmark, registra um retorno de chamada, e espera em Bookmark a ser continuado. Quando ela é retomada, a ReadLine atividade atribui os dados que foram passados com o Bookmark ao seu argumento Result. Neste exemplo, um fluxo de trabalho é criado que usa a ReadLine atividade para coletar o nome do usuário e exibi-lo na janela do console.
Variable<string> name = new Variable<string>();
Activity wf = new Sequence
{
Variables = { name },
Activities =
{
new WriteLine
{
Text = "What is your name?"
},
new ReadLine
{
BookmarkName = "UserName",
Result = new OutArgument<string>(name)
},
new WriteLine
{
Text = new InArgument<string>((env) =>
("Hello, " + name.Get(env)))
}
}
};
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
idleEvent.Set();
};
// Run the workflow.
wfApp.Run();
// Wait for the workflow to go idle before gathering
// the user's input.
idleEvent.WaitOne();
// Gather the user's input and resume the bookmark.
// Bookmark resumption only occurs when the workflow
// is idle. If a call to ResumeBookmark is made and the workflow
// is not idle, ResumeBookmark blocks until the workflow becomes
// idle before resuming the bookmark.
BookmarkResumptionResult result = wfApp.ResumeBookmark("UserName",
Console.ReadLine());
// Possible BookmarkResumptionResult values:
// Success, NotFound, or NotReady
Console.WriteLine($"BookmarkResumptionResult: {result}");
Quando a ReadLine atividade é executada, ela cria um Bookmark chamado UserName e aguarda que o marcador seja retomado. O host coleta os dados desejados e, em seguida, retoma o Bookmark. O fluxo de trabalho é retomado, exibe o nome e, em seguida, é concluído.
O aplicativo host pode inspecionar o fluxo de trabalho para determinar se há marcadores ativos. Ele pode fazer isso chamando o método GetBookmarks de uma instância WorkflowApplication ou inspecionando o WorkflowApplicationIdleEventArgs no manipulador Idle.
O exemplo de código a seguir é semelhante ao exemplo anterior, exceto que os marcadores ativos são enumerados antes que o marcador seja retomado. O fluxo de trabalho é iniciado, e depois que Bookmark é criado e fluxo de trabalho aparece ociosa, GetBookmarks é chamado. Quando o fluxo de trabalho é concluído, a saída a seguir é exibida no console.
Qual é o seu nome?
BookmarkName: UserName - OwnerDisplayName: ReadLineSteveHello, Steve
Variable<string> name = new Variable<string>();
Activity wf = new Sequence
{
Variables = { name },
Activities =
{
new WriteLine
{
Text = "What is your name?"
},
new ReadLine
{
BookmarkName = "UserName",
Result = new OutArgument<string>(name)
},
new WriteLine
{
Text = new InArgument<string>((env) =>
("Hello, " + name.Get(env)))
}
}
};
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
// You can also inspect the bookmarks from the Idle handler
// using e.Bookmarks
idleEvent.Set();
};
// Run the workflow.
wfApp.Run();
// Wait for the workflow to go idle and give it a chance
// to create the Bookmark.
idleEvent.WaitOne();
// Inspect the bookmarks
foreach (BookmarkInfo info in wfApp.GetBookmarks())
{
Console.WriteLine($"BookmarkName: {info.BookmarkName} - OwnerDisplayName: {info.OwnerDisplayName}");
}
// Gather the user's input and resume the bookmark.
wfApp.ResumeBookmark("UserName", Console.ReadLine());
O exemplo de código inspeciona WorkflowApplicationIdleEventArgs passado para o manipulador de Idle de uma instância de WorkflowApplication . Nesse exemplo a ociosa indo de fluxo de trabalho tem um Bookmark com um nome de EnterGuess, possuídas por uma atividade chamada ReadInt. Este exemplo de código é baseado em Como executar um fluxo de trabalho, que faz parte do Tutorial de Introdução. Se o Idle manipulador nessa etapa for modificado para conter o código deste exemplo, a saída a seguir será exibida.
BookmarkName: EnterGuess - OwnerDisplayName: ReadInt
wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
foreach (BookmarkInfo info in e.Bookmarks)
{
Console.WriteLine($"BookmarkName: {info.BookmarkName} - OwnerDisplayName: {info.OwnerDisplayName}");
}
idleEvent.Set();
};
Resumo
WorkflowInvoker fornece uma maneira leve de invocar fluxos de trabalho e, embora forneça métodos para transmitir dados no início de um fluxo de trabalho e extrair dados de um fluxo de trabalho concluído, ele não fornece cenários mais complexos que são onde WorkflowApplication podem ser usados.