Usando WorkflowInvoker e WorkflowApplication

O WF (Windows Workflow Foundation) fornece vários métodos de hospedagem de fluxos de trabalho. WorkflowInvoker fornece uma maneira simples para chamar um fluxo de trabalho como se fosse uma chamada de método e pode ser usado somente para os fluxos de trabalho que não usam persistência. WorkflowApplication fornece um modelo mais rico para executar fluxos de trabalho que inclui notificação de eventos de ciclo de vida, controle de execução, de ressunção do indexador, e de persistência. WorkflowServiceHost fornece suporte para atividades de mensagem e é basicamente usado com serviços de fluxo de trabalho. Este tópico apresenta o fluxo de trabalho que hospeda com WorkflowInvoker e WorkflowApplication. Para obter mais informações sobre como hospedar fluxos de trabalho com WorkflowServiceHost, confira Serviços de fluxo de trabalho e Visão geral dos serviços de fluxo de trabalho de hospedagem.

Usando WorkflowInvoker

WorkflowInvoker fornece um modelo para executar um fluxo de trabalho como se fosse um chamada de método. Para chamar um fluxo de trabalho usando WorkflowInvoker, chame o método de Invoke e passe a definição de fluxo de trabalho de fluxo de trabalho para chamar. Nesse exemplo, uma atividade de WriteLine é chamada usando WorkflowInvoker.

Activity wf = new WriteLine
{
    Text = "Hello World."
};

WorkflowInvoker.Invoke(wf);

Quando um fluxo de trabalho é chamado usando WorkflowInvoker, o fluxo de trabalho é executado no segmento de chamada e os blocos de método Invoke até que o fluxo de trabalho está completo, 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 . Nesse exemplo, um fluxo de trabalho é chamado duas vezes em 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.

Argumentos de entrada de configuração 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. Nesse exemplo, WriteLine é chamado e o valor para o argumento de Text é especificado usando o dicionário de parâmetros.

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

Parâmetros de saída de um fluxo de trabalho podem ser obtidos usando o dicionário de saída que é retornado da chamada a Invoke. O exemplo a seguir chama um fluxo de trabalho que consiste em uma única atividade de Divide que tem dois argumentos conectados e dois argumentos de saída. Quando o fluxo de trabalho é chamado, o dicionário de arguments é passado que contém os valores para cada argumento de entrada, fechado pelo nome do argumento. Quando o Invoke a chamada retorna, cada argumento de saída é retornado no dicionário de outputs , também fechado 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("{0} / {1} = {2} Remainder {3}",
    dividend, divisor, outputs["Result"], outputs["Remainder"]);

Se o fluxo de trabalho deriva de ActivityWithResult, como CodeActivity<TResult> ou Activity<TResult>, e há argumentos de saída além do argumento bem definido de saída de Result , uma sobrecarga não genérico de Invoke deve ser usada para recuperar os argumentos adicionais. Para fazer isso, a definição de fluxo de trabalho passada em Invoke deve ser do tipo Activity. Nesse exemplo a atividade de Divide deriva de CodeActivity<int>, mas é declarada como Activity de modo que uma sobrecarga não genérico de Invoke é usada que retorna um dicionário dos argumentos em vez de um único valor de retorno.

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("{0} / {1} = {2} Remainder {3}",
    dividend, divisor, outputs["Result"], outputs["Remainder"]);

Usando WorkflowApplication

WorkflowApplication fornece um conjunto rico de recursos para gerenciamento de instância 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 que você cria WorkflowApplication, assinar os eventos desejados do ciclo de vida, a inicia o fluxo de trabalho, e espera-o em concluir. Nesse exemplo, uma definição de fluxo de trabalho que consiste em uma atividade de WriteLine é criada e WorkflowApplication são criados usando a definição especificada de fluxo de trabalho. Completed é tratado para que o host é notificado quando o fluxo de trabalho for concluída, o fluxo de trabalho é iniciado com uma chamada a Run, e então o host espera o fluxo de trabalho para concluir. Quando o fluxo de trabalho terminar, AutoResetEvent é ajustado e o aplicativo host pode continuar a execução, conforme mostrado no exemplo o 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 de ciclo de vida de 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 tratar essas notificações e a ação apropriada, conforme mostrado no exemplo o seguir.

wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
    if (e.CompletionState == ActivityInstanceState.Faulted)
    {
        Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
        Console.WriteLine("Exception: {0}\n{1}",
            e.TerminationException.GetType().FullName,
            e.TerminationException.Message);
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
    }
    else
    {
        Console.WriteLine("Workflow {0} Completed.", e.InstanceId);

        // Outputs can be retrieved from the Outputs dictionary,
        // keyed by argument name.
        // Console.WriteLine("The winner is {0}.", e.Outputs["Winner"]);
    }
};

wfApp.Aborted = delegate(WorkflowApplicationAbortedEventArgs e)
{
    // Display the exception that caused the workflow
    // to abort.
    Console.WriteLine("Workflow {0} Aborted.", e.InstanceId);
    Console.WriteLine("Exception: {0}\n{1}",
        e.Reason.GetType().FullName,
        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 {0} Idle.", e.InstanceId);
};

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 {0} Unloaded.", e.InstanceId);
};

wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
    // Display the unhandled exception.
    Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
        e.InstanceId, e.UnhandledException.Message);

    Console.WriteLine("ExceptionSource: {0} - {1}",
        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;
};

Argumentos de entrada de configuração de um fluxo de trabalho

Os dados podem ser passados em um fluxo de trabalho como são iniciados usar um dicionário de parâmetros, semelhante à forma que os dados são passados na o usar WorkflowInvoker. Cada item em mapas de dicionário para um argumento de entrada de fluxo de trabalho especificado. Nesse exemplo, um fluxo de trabalho que consiste em uma atividade de WriteLine é chamado e o argumento de Text são especificados usando o dicionário de parâmetros.

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 concluir, os argumentos de saída podem ser recuperados no manipulador de Completed acessando o dicionário de WorkflowApplicationCompletedEventArgs.Outputs . O exemplo a seguir hospeda um fluxo de trabalho usando WorkflowApplication. Uma instância de WorkflowApplication é construída usando uma definição de fluxo de trabalho que consiste em uma só atividade DiceRoll. 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 for concluído, a saída são recuperadas no manipulador de Completed .

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 {0} Terminated.", e.InstanceId);
         Console.WriteLine("Exception: {0}\n{1}",
             e.TerminationException.GetType().FullName,
             e.TerminationException.Message);
     }
     else if (e.CompletionState == ActivityInstanceState.Canceled)
     {
         Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
     }
     else
     {
         Console.WriteLine("Workflow {0} Completed.", e.InstanceId);

         // Outputs can be retrieved from the Outputs dictionary,
         // keyed by argument name.
         Console.WriteLine("The two dice are {0} and {1}.",
             e.Outputs["D1"], e.Outputs["D2"]);
     }
 };

// Run the workflow.
 wfApp.Run();

Observação

WorkflowApplication e WorkflowInvoker têm um dicionário de argumentos de entrada e retornam um dicionário de argumentos de out . Esses parâmetros, propriedades, e valores de retorno do dicionário são do tipo IDictionary<string, object>. A instância real da classe dicionário que é passada em pode ser qualquer classe que implemente IDictionary<string, object>. Nesses exemplos, Dictionary<string, object> é usado. Para obter mais informações sobre dicionários, confira IDictionary<TKey,TValue> e Dictionary<TKey,TValue>.

Passando dados em um fluxo de trabalho em execução usando indicadores

Indexadores são o mecanismo por que uma atividade passiva pode esperar para ser continuado e é um mecanismo para passar dados em uma instância em execução de fluxo de trabalho. Se uma atividade está aguardando dados, Bookmark pode criar e registrar um método callback a ser chamado quando Bookmark é que, conforme mostrado no exemplo o 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 é continuada, a atividade de ReadLine atribui os dados que foram passados com Bookmark ao seu argumento de Result . Nesse exemplo, um fluxo de trabalho é criado que usa a atividade de ReadLine para coletar o nome de usuário e para o exibir a 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: {0}", result);

Quando a atividade de ReadLine é executada, cria Bookmark chamado UserName e espera no indexador a ser continuado. O host reúne os dados desejados e depois Bookmark. Retoma de fluxo de trabalho, exibe o nome, e então usa.

O aplicativo host pode inspecionar o fluxo de trabalho para determinar se houver qualquer marcador ativo. Pode fazer isso chamando o método de GetBookmarks de uma instância de WorkflowApplication , ou inspecionando WorkflowApplicationIdleEventArgs no manipulador de Idle .

O exemplo de código é como o exemplo anterior exceto que os indicadores ativas são enumerados antes que o indexador é continuado. O fluxo de trabalho é iniciado, e depois que Bookmark é criado e fluxo de trabalho aparece ociosa, GetBookmarks é chamado. Quando o fluxo de trabalho for concluído, a saída a seguir são exibidas no console.

Que é o nome?
BookmarkName: UserName – OwnerDisplayName: ReadLineMateusOlá, Mateus

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: {0} - OwnerDisplayName: {1}",
        info.BookmarkName, 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 manipulador de Idle nessa etapa é alterado para conter o código deste exemplo, a seguinte saída são exibidas.

BookmarkName: EnterGuess - OwnerDisplayName: ReadInt

wfApp.Idle = delegate(WorkflowApplicationIdleEventArgs e)
{
    foreach (BookmarkInfo info in e.Bookmarks)
    {
        Console.WriteLine("BookmarkName: {0} - OwnerDisplayName: {1}",
            info.BookmarkName, info.OwnerDisplayName);
    }

    idleEvent.Set();
};

Resumo

WorkflowInvoker fornece uma maneira leve de invocar fluxos de trabalho e, embora fornece métodos para passar dados no início de um fluxo de trabalho e extrair dados de um fluxo de trabalho concluído, não fornece cenários mais complexos que é onde WorkflowApplication pode ser usado.