Partilhar via


Usando WorkflowInvoker e WorkflowApplication

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 a execução de fluxos de trabalho que inclui notificação de eventos do ciclo de vida, controle de execução, retomada de marcadores 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 WorkflowServiceHosto , consulte Visão geral dos Serviços de Fluxo de Trabalho e dos Serviços de Fluxo de Trabalho de Hospedagem.

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 WorkflowInvokero , 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 WorkflowInvokero , 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 no qual o fluxo de trabalho deve ser concluído, use uma das Invoke sobrecargas que usa um TimeSpan parâmetro. 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);
}

Nota

O TimeoutException só é lançado se o intervalo de tempo limite decorrer e o fluxo de trabalho ficar ocioso durante a execução. Um fluxo de trabalho que leva mais tempo do que o intervalo de tempo limite especificado para ser concluído com êxito se o fluxo de trabalho não ficar ocioso.

WorkflowInvoker Também fornece versões assíncronas do método Invoke. Para obter mais informações, consulte InvokeAsync e BeginInvoke.

Definindo argumentos de entrada de um fluxo de trabalho

Os dados podem ser passados para um fluxo de trabalho usando um dicionário de parâmetros de entrada, digitados pelo nome do argumento, que são mapeados para os argumentos de entrada do fluxo de trabalho. Neste exemplo, a WriteLine é invocado e o valor para 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 usando o dicionário de saídas retornado da 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, é passado o arguments dicionário que contém os valores para cada argumento de entrada, digitado pelo nome do argumento. Quando a chamada para Invoke retorna, cada argumento de saída é retornado no dicionário, também digitado pelo nome do outputs 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 derivar de , como ou , Activity<TResult>e houver argumentos de ActivityWithResultsaída além do argumento de saída bem definidoResult, uma sobrecarga não genérica de Invoke deverá ser usada para recuperar os argumentos adicionais.CodeActivity<TResult> Para fazer isso, a definição de fluxo de trabalho passada para Invoke deve ser do tipo Activity. Neste exemplo, a atividade deriva Divide de CodeActivity<int>, mas é declarada como Activity de modo que uma sobrecarga não genérica de Invoke é usada que retorna um dicionário de 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 avançado de recursos para gerenciamento de instâncias de fluxo de trabalho. WorkflowApplication Atua como um proxy seguro de thread para o real WorkflowInstance, que encapsula o tempo de execução e fornece métodos para criar e carregar instâncias de fluxo de trabalho, pausar e retomar, encerrar e notificar eventos do ciclo de vida. Para executar um fluxo de trabalho usando WorkflowApplication o WorkflowApplication, assine os eventos de ciclo de vida desejados, inicie o fluxo de trabalho e aguarde sua conclusão. Neste exemplo, uma definição de fluxo de trabalho que consiste em uma WriteLine atividade é criada e uma WorkflowApplication é criada usando a definição de fluxo de trabalho especificada. Completed é manipulado para que o host seja notificado quando o fluxo de trabalho for concluído, o fluxo de trabalho for iniciado com uma chamada para e, em Runseguida, o host aguardará a conclusão do fluxo de trabalho. Quando o fluxo de trabalho for concluído, o AutoResetEvent será definido e o aplicativo host 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();

Fluxo de trabalhoEventos do ciclo de vida do aplicativo

Além do , os autores de Completedhost podem ser notificados quando um fluxo de trabalho é descarregado (Unloaded), abortado (Aborted), fica ocioso (Idle e PersistableIdle), ou ocorre uma exceção não tratada (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 {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;
};

Definindo argumentos de entrada de um fluxo de trabalho

Os dados podem ser passados para um fluxo de trabalho à medida que são iniciados usando um dicionário de parâmetros, semelhante à maneira como os dados são passados ao usar WorkflowInvokero . 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 Completed no manipulador acessando o WorkflowApplicationCompletedEventArgs.Outputs dicionário. O exemplo a seguir hospeda um fluxo de trabalho usando WorkflowApplicationo . Uma WorkflowApplication instância é construída usando uma definição de fluxo de trabalho que consiste em uma única DiceRoll atividade. A DiceRoll atividade 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 Completed no 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 {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();

Nota

WorkflowApplication e WorkflowInvoker pegue um dicionário de argumentos de entrada e retorne um dicionário de out argumentos. Esses parâmetros de dicionário, propriedades e valores de retorno são do tipo IDictionary<string, object>. A instância real da classe de dicionário que é passada pode ser qualquer classe que implementa 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

Os 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 e Bookmark registrar um método de retorno de chamada a ser chamado quando o 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 ReadLine atividade cria um Bookmark, registra um retorno de chamada e, em seguida, aguarda que o Bookmark seja retomado. Quando é retomada, a ReadLine atividade atribui os dados que foram passados com o Bookmark seu Result argumento. Neste exemplo, é criado um fluxo de trabalho 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: {0}", result);

Quando a ReadLine atividade é executada, ela cria um Bookmark nome UserName e, em seguida, 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 é concluído.

O aplicativo host pode inspecionar o fluxo de trabalho para determinar se há marcadores ativos. Ele pode fazer isso chamando o GetBookmarks método de uma WorkflowApplication instância ou inspecionando o WorkflowApplicationIdleEventArgs Idle no manipulador.

O exemplo de código a seguir é como o exemplo anterior, exceto que os marcadores ativos são enumerados antes que o indicador seja retomado. O fluxo de trabalho é iniciado e, uma vez que o Bookmark é criado e o fluxo de trabalho fica ocioso, GetBookmarks é chamado. Quando o fluxo de trabalho é concluído, a saída a seguir é exibida no console.

Qual é o teu nome?
BookmarkName: UserName - OwnerDisplayName: ReadLineSteveOlá, 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: {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 a seguir inspeciona o WorkflowApplicationIdleEventArgs passado para o Idle manipulador de uma WorkflowApplication instância. Neste exemplo, o fluxo de trabalho ocioso tem um Bookmark com um nome de , de propriedade de EnterGuessuma 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: {0} - OwnerDisplayName: {1}",
            info.BookmarkName, info.OwnerDisplayName);
    }

    idleEvent.Set();
};

Resumo

WorkflowInvoker fornece uma maneira leve de invocar fluxos de trabalho e, embora forneça métodos para passar 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 é onde WorkflowApplication pode ser usado.