Partilhar via


Criando atividades assíncronas no WF

AsyncCodeActivity Fornece aos autores de atividade uma classe base a ser usada que permite que atividades derivadas implementem lógica de execução assíncrona. Isso é útil para atividades personalizadas que devem executar trabalho assíncrono sem segurar o thread do agendador de fluxo de trabalho e bloquear quaisquer atividades que possam ser executadas em paralelo. Este tópico fornece uma visão geral de como criar atividades assíncronas personalizadas usando o AsyncCodeActivity.

Usando AsyncCodeActivity

System.Activities Fornece aos autores de atividades personalizadas diferentes classes base para diferentes requisitos de criação de atividades. Cada um carrega uma semântica específica e fornece a um autor de fluxo de trabalho (e o tempo de execução da atividade) um contrato correspondente. Uma AsyncCodeActivity atividade baseada é uma atividade que executa trabalho de forma assíncrona em relação ao thread do agendador e cuja lógica de execução é expressa em código gerenciado. Como resultado da assíncrona, um pode induzir um ponto ocioso AsyncCodeActivity durante a execução. Devido à natureza volátil do trabalho assíncrono, um AsyncCodeActivity sempre cria um bloco sem persistência durante a execução da atividade. Isso impede que o tempo de execução do fluxo de trabalho persista a instância do fluxo de trabalho no meio do trabalho assíncrono e também impede que a instância do fluxo de trabalho seja descarregada enquanto o código assíncrono está em execução.

Métodos AsyncCodeActivity

As atividades derivadas podem criar lógica de AsyncCodeActivity execução assíncrona substituindo os BeginExecute métodos e EndExecute pelo código personalizado. Quando chamados pelo tempo de execução, esses métodos são passados em um AsyncCodeActivityContextarquivo . AsyncCodeActivityContext permite que o autor da atividade forneça o estado compartilhado na BeginExecute/ EndExecute propriedade do UserState contexto. No exemplo a seguir, uma GenerateRandom atividade gera um número aleatório de forma assíncrona.

public sealed class GenerateRandom : AsyncCodeActivity<int>
{
    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Func<int> GetRandomDelegate = new Func<int>(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(callback, state);
    }

    protected override int EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Func<int> GetRandomDelegate = (Func<int>)context.UserState;
        return (int)GetRandomDelegate.EndInvoke(result);
    }

    int GetRandom()
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        return r.Next(1, 101);
    }
}

A atividade de exemplo anterior deriva de AsyncCodeActivity<TResult>, e tem um nome Resultelevado OutArgument<int> . O valor retornado pelo GetRandom método é extraído EndExecute e retornado pela substituição, e esse valor é definido como o Result valor. As atividades assíncronas que não retornam um resultado devem derivar de AsyncCodeActivity. No exemplo a seguir, é definida uma DisplayRandom atividade que deriva de AsyncCodeActivity. Essa atividade é como a atividade, GetRandom mas em vez de retornar um resultado, ela exibe uma mensagem para o console.

public sealed class DisplayRandom : AsyncCodeActivity
{
    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Action GetRandomDelegate = new Action(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Action GetRandomDelegate = (Action)context.UserState;
        GetRandomDelegate.EndInvoke(result);
    }

    void GetRandom()
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        Console.WriteLine("Random Number: {0}", r.Next(1, 101));
    }
}

Observe que, como não há nenhum valor de retorno, DisplayRandom usa um Action em vez de um Func<T,TResult> para invocar seu delegado, e o delegado não retorna nenhum valor.

AsyncCodeActivity também fornece uma Cancel substituição. Enquanto BeginExecute e EndExecute são substituições obrigatórias, Cancel é opcional e pode ser substituída para que a atividade possa limpar seu estado assíncrono pendente quando estiver sendo cancelada ou abortada. Se a limpeza é possível e AsyncCodeActivity.ExecutingActivityInstance.IsCancellationRequested é true, a atividade deve chamar MarkCanceled. Quaisquer exceções lançadas desse método são fatais para a instância do fluxo de trabalho.

protected override void Cancel(AsyncCodeActivityContext context)
{
    // Implement any cleanup as a result of the asynchronous work
    // being canceled, and then call MarkCanceled.
    if (context.IsCancellationRequested)
    {
        context.MarkCanceled();
    }
}

Invocando métodos assíncronos em uma classe

Muitas das classes no .NET Framework fornecem funcionalidade assíncrona, e essa funcionalidade pode ser invocada de forma assíncrona usando uma AsyncCodeActivity atividade baseada. No exemplo a seguir, é criada uma atividade que cria de forma assíncrona um arquivo usando a FileStream classe.

public sealed class FileWriter : AsyncCodeActivity
{
    public FileWriter()
        : base()
    {
    }

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        string tempFileName = Path.GetTempFileName();
        Console.WriteLine("Writing to file: " + tempFileName);

        FileStream file = File.Open(tempFileName, FileMode.Create);

        context.UserState = file;

        byte[] bytes = UnicodeEncoding.Unicode.GetBytes("123456789");
        return file.BeginWrite(bytes, 0, bytes.Length, callback, state);
    }

    protected override void EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        FileStream file = (FileStream)context.UserState;

        try
        {
            file.EndWrite(result);
            file.Flush();
        }
        finally
        {
            file.Close();
        }
    }
}

Estado de compartilhamento entre os métodos BeginExecute e EndExecute

No exemplo anterior, o FileStream objeto que foi criado em BeginExecute foi acessado EndExecuteno . Isso é possível porque a file variável foi passada na AsyncCodeActivityContext.UserState propriedade em BeginExecute. Este é o método correto para compartilhar o estado entre BeginExecute e EndExecute. É incorreto usar uma variável membro na classe derivada (FileWriter neste caso) para compartilhar o estado entre BeginExecute e EndExecute porque o objeto de atividade pode ser referenciado por várias instâncias de atividade. A tentativa de usar uma variável membro para compartilhar o estado pode resultar em valores de uma ActivityInstance substituição ou no consumo de valores de outra ActivityInstance.

Acessando valores de argumento

O ambiente de um AsyncCodeActivity consiste nos argumentos definidos sobre a atividade. Esses argumentos podem ser acessados a BeginExecute/EndExecute partir das substituições usando o AsyncCodeActivityContext parâmetro. Os argumentos não podem ser acessados no delegado, mas os valores do argumento ou quaisquer outros dados desejados podem ser passados para o delegado usando seus parâmetros. No exemplo a seguir, uma atividade geradora de números aleatórios é definida que obtém o limite superior inclusivo de seu Max argumento. O valor do argumento é passado para o código assíncrono quando o delegado é invocado.

public sealed class GenerateRandomMax : AsyncCodeActivity<int>
{
    public InArgument<int> Max { get; set; }

    static Random r = new Random();

    protected override IAsyncResult BeginExecute(AsyncCodeActivityContext context, AsyncCallback callback, object state)
    {
        // Create a delegate that references the method that implements
        // the asynchronous work. Assign the delegate to the UserState,
        // invoke the delegate, and return the resulting IAsyncResult.
        Func<int, int> GetRandomDelegate = new Func<int, int>(GetRandom);
        context.UserState = GetRandomDelegate;
        return GetRandomDelegate.BeginInvoke(Max.Get(context), callback, state);
    }

    protected override int EndExecute(AsyncCodeActivityContext context, IAsyncResult result)
    {
        // Get the delegate from the UserState and call EndInvoke
        Func<int, int> GetRandomDelegate = (Func<int, int>)context.UserState;
        return (int)GetRandomDelegate.EndInvoke(result);
    }

    int GetRandom(int max)
    {
        // This activity simulates taking a few moments
        // to generate the random number. This code runs
        // asynchronously with respect to the workflow thread.
        Thread.Sleep(5000);

        return r.Next(1, max + 1);
    }
}

Agendando ações ou atividades filho usando AsyncCodeActivity

AsyncCodeActivity As atividades personalizadas derivadas fornecem um método para executar o trabalho de forma assíncrona em relação ao thread do fluxo de trabalho, mas não fornecem a capacidade de agendar atividades ou ações filhas. No entanto, o comportamento assíncrono pode ser incorporado com o agendamento das atividades da criança através da composição. Uma atividade assíncrona pode ser criada e, em seguida, composta com uma Activity atividade derivada para NativeActivity fornecer comportamento assíncrono e agendamento de atividades ou ações da criança. Por exemplo, pode ser criada uma atividade que deriva de Activity, e tem como implementação uma Sequence contendo a atividade assíncrona, bem como as outras atividades que implementam a lógica da atividade. Para obter mais exemplos de como compor atividades usando Activity e , consulte Como criar uma atividade e opções de NativeActivitycriação de atividade.

Consulte também