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 Result
elevado 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.