Este artigo foi traduzido por máquina.

Fundações

Erro de manipulação em fluxos de trabalho

Matt Milner

Download do código disponível na Galeria de código do MSDN
Procure o código on-line

Conteúdo

Tratamento de falhas nos fluxos de trabalho
Tratamento de falhas no processo de host
Tratamento de falhas em atividades personalizadas
Usando a compensação
Repetir a atividade

Windows Workflow Foundation (WF) fornece as ferramentas para definir processos de negócios rich e um ambiente de tempo de execução para executar e gerenciar esses processos. Na qualquer processo de negócios, ocorrem exceções para o fluxo esperada de execução e os desenvolvedores precisam ser capazes de escrever lógica de aplicativos robusto para recuperar essas exceções. A maioria dos exemplos de qualquer tecnologia tendem a ignorar o tratamento de falhas e recuperação apropriada. Na edição deste mês, mostrarei como manipular exceções corretamente em vários níveis do WF modelo de programação e como criar exceção sofisticada recursos de manipulação em seus fluxos de trabalho e os hosts.

Tratamento de falhas nos fluxos de trabalho

Os desenvolvedores a criação de processos de negócios devem ser capaz de manipular exceções para o caso de negócios para garantir que o próprio processo seja resistente e podem continuar após a ocorrem de falhas. Isso é especialmente importante com os fluxos de trabalho, como eles geralmente definem processos de execução demorada, e uma falha não tratada, na maioria dos casos, significa ter que reiniciar o processo. O comportamento padrão de um fluxo de trabalho é que se uma exceção acontece no fluxo de trabalho e não ela for tratada, o fluxo de trabalho será encerrado. Portanto, é crucial para os desenvolvedores fluxo de trabalho corretamente escopo de trabalho, lidar com erros e promover os fluxos de trabalho a capacidade de repetir trabalho quando ocorrem falhas.

Tratamento de falhas nos fluxos de trabalho tem muitas coisas em comum com tratamento de falhas no código do Microsoft .NET Framework-alvo e alguns novos conceitos. Para lidar com uma falha, a primeira etapa é definir um escopo de execução. No código do .NET, isso é feito com a palavra-chave try. Nos fluxos de trabalho, a maioria das atividades compostas podem ser usadas para criar uma escopo de manipulação de exceção. Cada atividade composta tem o modo de exibição principal, mostrando o filho atividades e também tem exibições alternativas. Na Figura 1 o menu de contexto em uma atividade de seqüência mostra como diversos modos de exibição podem ser acessados e o resultado de selecionar a opção de manipuladores de falha de modo de exibição.

fig01.gif

Figura 1 alternativo menu de modos de exibição e manipuladores de erro de SelectingView

Ao alternar para a exibição de manipuladores de erro, uma atividade FaultHandlers é adicionada à coleção atividades da atividade de seqüência. Dentro da atividade de FaultHandlers, atividades FaultHandler individuais podem ser adicionadas. Cada das atividades FaultHandler tem uma propriedade para definir o tipo de falha e funciona como uma expressão de catch no. NET.

A atividade FaultHandler é uma atividade composta que permite aos desenvolvedores de fluxo de trabalho adicionar atividades filho que definem como lidar com a exceção. Essas atividades podem fornecer funcionalidade para fizer erros, os administradores de contatos ou qualquer outra ação que você normalmente pode levar ao tratamento de exceções em seu código.

A atividade FaultHandler também tem uma propriedade de falha que contém a exceção que está sendo detectada. Atividades filho podem vincular a essa propriedade para obter acesso para a exceção. Isso é ilustrado na Figura 2 , onde uma atividade de log personalizada tem sua propriedade de exceção ligado à propriedade Falha na atividade FaultHandler. A atividade de log agora pode escrever as informações da exceção para um log de API, o log de eventos do Windows, de WMI ou qualquer outro destino.

fig02.gif

A Figura 2 ligação para falhas

Como capturar blocos, as atividades FaultHandler são avaliadas com base nos seus tipos de falhas. Ao definir o fluxo de trabalho, as atividades FaultHandler devem ser adicionadas para o FaultHandlers na ordem na mais específica para o menos específico, da esquerda para a direita.

fig03.gif

A Figura 3 execução continua após o composto

Quando uma exceção ocorre e é detectada no código do .NET, depois que o bloco catch termina a execução continua após o escopo try. Portanto, em um fluxo de trabalho, a execução continua com a próxima atividade após a atividade composta que manipule a exceção (consulte a Figura 3 ).

Há dois conceitos chaves sobre como as atividades FaultHandler avaliadas e executadas. Quando a atividade Throw executa (como na Figura 3 ) ou outra atividade lança uma exceção, o runtime coloca a atividade no estado de faulted e agenda a atividade para executar o método HandleFault. Entrará em mais detalhes no como atividades implementar esse método em breve, mas para agora é suficiente para saber que isso é a chance da atividade para limpar.

Quando a atividade termina limpeza e move para o estado fechado, a atividade pai é colocada em estado de falha e da mesma forma é fornecida a oportunidade de limpar quaisquer atividades filho e sinal de que ele está pronto para mover para o estado fechado. É nesse ponto, quando a atividade composta sinaliza que ele está pronto para mover para o estado fechado, que o runtime verifica o status e, quando it is Faulting, examina a coleção FaultHandlers. Se uma atividade FaultHandler for localizada com um tipo de falha que coincide com a exceção atual, que FaultHandler é programado e a avaliação é interrompida. Depois de fecha o FaultHandler, execução, em seguida, pode continuar a próxima atividade.

Quando ocorre uma exceção, o tempo de execução tentará localizar atividades de tratamento de falhas na atividade composta pai imediato. Se não manipuladores correspondentes for localizadas, que composição é falhar e a exceção bubbles até a próxima atividade composta na árvore de. Isso é semelhante a como exceções .NET Emergir da pilha para os métodos de chamada quando elas estiverem sem tratamento. Se a exceção bubbles todo para a raiz atividade de fluxo de trabalho e nenhum manipulador é encontrada, o fluxo de trabalho é finalizado.

Observe que o próprio fluxo de trabalho é uma atividade composta e, portanto, pode ter lógica de tratamento de falhas definida para lidar com exceções que alcançar o nível superior. Essa é a última chance de um desenvolvedor de fluxo de trabalho para capturar e manipular exceções no processo de negócios.

Tratamento de falhas no processo de host

Embora seja importante criar fluxos de trabalho robustos, com um host que pode lidar com exceções é igualmente importante a estabilidade do seu aplicativo. Felizmente, o tempo de execução do fluxo de trabalho é robusto ao manipular essas exceções sair da caixa de e protege o processo de host da maioria das exceções subida backup.

Quando uma exceção ocorre em um fluxo de trabalho, bolhas backup por meio da hierarquia do e é não identificada, o fluxo de trabalho será finalizada e um evento é disparado no runtime. O host do tempo de execução pode registrar um manipulador para ser notificado quando essas exceções ocorrem, mas as exceções não causam o host a falha. Para ser notificado sobre esses demissões, o processo de host pode usar código como o seguinte para obter informações sobre a exceção dos argumentos do evento:

workflowRuntime.WorkflowTerminated += delegate(
  object sender, WorkflowTerminatedEventArgs e)
{
  Console.WriteLine(e.Exception.Message);
};

Em adição para lidar com fluxos de trabalho demitidos, o processo de host também possui a capacidade de ser notificado sobre exceções que ocorrem em serviços de tempo de execução. Por exemplo, se SqlWorkflowPersistence­Service é carregado no tempo de execução, ele irá pesquisar o banco de dados e pode tentar carregar os fluxos de trabalho periodicamente quando eles têm mais trabalho a fazer. Ao tentar carregar um fluxo de trabalho, o serviço de persistência pode lançar uma exceção ao tentar desserializar o fluxo de trabalho, por exemplo. Quando isso acontece, novamente é importante que o processo de host não falhar, por isso que esses serviços não relançar essas exceções. Em vez disso, eles dispara um evento ao tempo de execução do fluxo de trabalho. O tempo de execução em contrapartida dispara um evento ServicesExceptionNotHandled que pode ser manipulado no código, como mostrado aqui:

workflowRuntime.ServicesExceptionNotHandled += delegate(
  object sender, ServicesExceptionNotHandledEventArgs snhe)
{
  Console.WriteLine(snhe.Exception.Message);
};

Em geral, os desenvolvedores de serviços de tempo de execução precisam fazer uma escolha Quando capturar uma exceção sobre se a exceção é crítica. Em SqlWorkflowPersistenceService, impossibilidade de carregar um único fluxo de trabalho não significa que o serviço não funcionará. Portanto, faz sentido nesse caso para simplesmente disparar um evento para permitir que o processo de host determinar se ainda mais ação é necessária. No entanto, se o serviço de persistência não é possível se conectar ao banco de dados do SQL Server, em seguida, ele não pode funcionar em todos os. Nesse caso, em vez de disparar um evento, faz mais sentido para o serviço lançar uma exceção e coloque o host parar para que o problema pode ser resolvido.

Ao desenvolver serviços de tempo de execução personalizada, a abordagem recomendada é com os serviços que derivam da classe base WorkflowRuntimeService. Essa classe de base fornece que dois acesso ao tempo de execução e um método protegido para disparar um evento ServicesExceptionNotHandled. Quando uma exceção ocorre na execução de um serviço de tempo de execução, o serviço deve exibir somente essa exceção se for realmente um erro irrecuperável. Se o erro está relacionado a uma instância de fluxo de trabalho única e não geral execução do serviço, em seguida, um evento deve ser gerado em vez disso.

Tratamento de falhas em atividades personalizadas

Para autores de atividade, manipulação de exceção assume um significado um pouco diferente. O objetivo com atividades de manipulação de exceção é dois: manipular exceções que ocorrem para mantê-los, quando possível, de subida backup e interromper o fluxo de trabalho e limpar corretamente em casos onde uma exceção não tratada bubbles fora da atividade.

Como uma atividade é simplesmente uma classe, tratamento de exceções dentro da atividade não é diferente em qualquer outra classe. Use Try/Catch blocos ao chamar outros componentes que pode lançar erros. No entanto, depois que você capturar uma exceção de uma atividade, você deve optar por relançar a exceção. Se a exceção é algo que não afetarão o resultado da atividade ou sua atividade, tem um mais controlada maneira de indicar que não foi bem-sucedida, esta é a maneira preferencial que comentários ". Se, no entanto, a exceção significa que sua atividade falhou e não pode concluir seu processamento ou forneça uma indicação de falha, você deve lançar uma exceção, o desenvolvedor do fluxo de trabalho pode criar o processo comercial para manipular a exceção.

O aspecto de manipular exceções em atividades está lidando com limpeza de recursos de atividade. Ao contrário de um fluxo de trabalho, onde o tratamento de falhas enfoca o processo de negócios, log e notificação, tratamento de falhas em atividades principalmente enfoca limpando os recursos usados na execução da atividade.

A maneira de você tratar falhas também dependerá se você estiver escrevendo uma atividade de folha ou uma atividade composta. Em uma atividade de folha, o método HandleFault é chamado quando uma exceção não tratada detectada pelo tempo de execução para permitir que a atividade para liberar os recursos que possam estar em uso e limpar qualquer execução que foi iniciada. Por exemplo, se a atividade estiver usando um banco de dados durante a execução, no método HandleFault ele verifique fechar a conexão se ele já não é fechado e descartar de outros recursos que podem estar em uso. Se a atividade foi iniciada qualquer trabalho assíncrono, poderia ser o tempo para cancelar trabalho e liberar os recursos sejam usados para esse processamento.

Para uma atividade composta, quando o método HandleFault ocorre, talvez seja devido a um erro lógica na atividade propriamente dito ou talvez seja porque uma atividade filho tem erradas. Em ambos os casos, a intenção em chamando o método HandleFault em uma atividade composta é permitir que a atividade para limpar suas atividades filho. Essa limpeza envolve certificando-se que a composição não solicita as atividades mais ser executadas e cancelar todas as atividades que estão executando. Felizmente, a implementação padrão do método HandleFault, definido na classe base Composite­Activity, é chamar o método de cancelar na atividade composta.

Cancelamento é outro mecanismo que permite atividades que tem iniciado alguns trabalhar de forma assíncrona e estão aguardando no momento para esse trabalho para concluir para ser notificado de que eles devem cancelar o trabalho que tem iniciado e limpar seus recursos para eles podem fechar. Uma atividade pode ser cancelada se outra atividade acionou uma falha, ou em circunstâncias normais se a lógica de fluxo de controle da atividade composta pai decidir cancelar o trabalho.

Quando uma atividade está a ser cancelado, o tempo de execução define o status dessa atividade como Canceling e chama o método de cancelar a atividade. Por exemplo, a atividade Replicator pode iniciar várias iterações de uma atividade filho, uma para cada parte dos dados fornecidos e agendar essas atividades sejam executados em paralelo. Ele também tem uma propriedade de Until­Condition que será avaliada como cada atividade filho fecha. É possível, e provavelmente essa avaliação do Until­Condition fará com que a atividade para determinar o que ele deve ser concluída.

Em ordem para o Replicator fechar, ele deve primeiro fechar todas as atividades filho. Como cada uma dessas atividades já foi agendada e potencialmente está sendo executado, a atividade Replicator verifica o valor atual da propriedade ExecutionStatus e, se ele is Executing, faz uma solicitação para o tempo de execução para cancelar a atividade.

Usando a compensação

Tratamento de falhas nos fluxos de trabalho permite aos desenvolvedores lidar com as condições de exceção imediata. O uso de transações também oferece a capacidade de trabalho de escopo para garantir a consistência. Entretanto, em execução longa fluxos de trabalho, é possível que duas unidades de trabalho exigem consistência, mas não é possível usar uma transação.

Por exemplo, quando inicia um fluxo de trabalho pode atualizar os dados em um aplicativo de linha de negócios, talvez adicionando um cliente no sistema CRM. Esse trabalho pode até mesmo ser parte de uma transação para proporcionar consistência entre várias operações no CRM e com o estado do fluxo de trabalho. Em seguida, após aguardando entrada mais de um usuário, que pode demorar dias para acontecer, o fluxo de trabalho atualiza um sistema de contabilidade com as informações do cliente. É importante que tanto o sistema de estatísticas e o sistema CRM ter dados consistentes, mas não é possível usar uma transação atômica para esses recursos em um intervalo de tempo grande. Portanto, torna-se a pergunta, como fazer lidar com exceções que ocorrem ao atualizar o sistema segundo para garantir a consistência com as alterações confirmadas já o primeiro sistema?

fig04.gif

A Figura 4 durante a atividade como repetir lógica

Como o trabalho em dois sistemas não pode ser feito consistente com uma transação, o que você precisa é um mecanismo para detectar erros que ocorrem ao atualizar o sistema segundo e oferecem a oportunidade para voltar e desfazer o trabalho aplicado no sistema inicial, ou caso contrário, fazer alterações para garantir a consistência. Embora o ato de detectar essa alteração e iniciar esse processo pode ser automático, o trabalho de corrigir o sistema inicial, obviamente, deve ser especificado pelo desenvolvedor.

No WF esse processo é conhecido para compensação e várias atividades são fornecidos para ajudar a desenvolver os fluxos de trabalho que usam a compensação. Para obter mais informações sobre compensação e como usar a compensação atividades relacionadas, consulte coluna Cutting Edge de Dino Esposito nos fluxos de trabalho transacionais de junho de 2007 da MSDN Magazine " Fluxos de trabalho transacionais").

Repetir a atividade

Um dos problemas com lidar com exceções em fluxos de trabalho estiver que, quando ocorre uma exceção, mesmo se você capturá-lo, a execução move para a próxima etapa no processo. Em muitos processos de negócios, execução, na verdade, não deve continuar até que a lógica comercial definida nos fluxos de trabalho é executado com êxito. Os desenvolvedores geralmente lidar com isso usando um durante atividade para fornecer lógica de repetição e definindo a condição da atividade para indicar que a atividade deve continuar a executar desde que ocorreu um erro. Além disso, uma atividade de atraso é freqüentemente usada para manter a lógica de repetição ocorra imediatamente.

Para habilitar esse modelo de repetição, você pode empregar uma atividade de seqüência como o filho de um durante atividades. Além disso, uma unidade de trabalho na seqüência específica é geralmente encapsulada em outra atividade de seqüência ou composição para manipular as exceções, que atua como o escopo de tratamento de falhas com todos os manipuladores fault definidos os manipuladores de erro exibir. Em seguida, uma atividade IfElse geralmente é utilizada para modificar o estado do fluxo de trabalho para influenciar a condição em While atividade.

No caso onde nenhuma exceção ocorre, a lógica de define uma propriedade ou sinalizar algum tipo tão While atividade pode fechar. Se uma exceção ocorreu, o sinalizador é definido como fazer com que o tempo atividade para executar novamente e uma atividade de atraso é usado para pausar antes de fazer a próxima tentativa. a Figura 4 mostra um exemplo de uso durante a atividade para repetir atividades em um fluxo de trabalho.

Embora esse padrão determinado funcione em vários cenários, imagine um fluxo de trabalho com 5 ou 10 operações diferentes que precisam ser repetida. Rapidamente você descobrirá que é muito trabalho para criar a lógica de repetição para cada atividade. Felizmente, o WF permite aos desenvolvedores escrever atividades personalizadas, incluindo atividades compostas personalizadas. Isso significa que POSSO escrever minha própria atividade ' repetir ' para encapsular a executar as atividades filho novamente quando ocorre uma exceção. Para isso ser valiosas, desejo fornecer duas entradas de chaves para usuários: um intervalo de atraso entre tentativas e um número máximo de vezes para repetir o trabalho antes de deixar a bolha exceção up e ser manipulada.

No restante desta coluna, explicará em detalhes a lógica na atividade de repetição. Para informações de plano de fundo como criar atividades personalizadas, consulte meu artigo anterior " Fluxo de trabalho do Windows: criar atividades personalizadas para estender o alcance de seus fluxos de trabalho" e para obter mais informações sobre como usar o Activity­ExecutionContext para criar atividades que podem iterar sobre uma atividade filho, consulte a edição de junho de 2007 desse (coluna " ActivityExecutionContext em fluxos de trabalho").

Para gerenciar a atividade filho corretamente, é importante poder monitorar a atividade saber quando ocorrerem erros. Portanto, ao executar a atividade filho, a atividade de repetição não só registra para obter notificado quando a atividade filho fecha, mas também registra para obter notificado quando a atividade filho é colocada em um estado Faulting. a Figura 5 mostra o método de BeginIteration usado para iniciar cada iteração da atividade filho. Antes de agendar a atividade, os eventos fechado e Faulting têm manipuladores registrados.

Figura 5 executar atividades filho e registro de falhas

Activity child = EnabledActivities[0];
ActivityExecutionContext newContext = 
  executionContext.ExecutionContextManager.CreateExecutionContext(child);

newContext.Activity.Closed += 
  new 
EventHandler<ActivityExecutionStatusChangedEventArgs>(child_Closed);

newContext.Activity.Faulting += 
  new 
EventHandler<ActivityExecutionStatusChangedEventArgs>(Activity_Faulting);

newContext.ExecuteActivity(newContext.Activity);

Normalmente se falhas uma atividade filho, a atividade pai também poderia ser colocar no estado de falha. Para evitar essa situação, quando as falhas de atividade filho, a atividade de repetição verifica ver se a atividade já foi tentada novamente o número máximo de vezes. Se a contagem retry não tiver sido alcançada, este código nulos fora a exceção atual na atividade filho, eliminando, portanto, a exceção:

void Activity_Faulting(object sender, 
  ActivityExecutionStatusChangedEventArgs e)
{
  e.Activity.Faulting -= Activity_Faulting;
  if(CurrentRetryAttempt < RetryCount) 
e.Activity.SetValue(
    ActivityExecutionContext.CurrentExceptionProperty, null);
}

Quando a atividade filho é fechado, a lógica deve determinar como a atividade tem para o estado fechado e usa a propriedade ExecutionResult para fazê-lo. Como todas as atividades finalizar no estado fechado, o ExecutionStatus não fornece as informações necessárias para determinar o resultado real, mas o ExecutionResult indica se a atividade falha, bem-sucedida ou foi cancelada. Se a atividade filho foi bem-sucedida, em seguida, não é necessária nenhuma repetição e simplesmente fecha a atividade de repetição:

if (e.ExecutionResult == ActivityExecutionResult.Succeeded)
{
  this.SetValue(ActivityExecutionContext.CurrentExceptionProperty, null);
  thisContext.CloseActivity();
  return;
}

Se o resultado da atividade de fechamento não for bem-sucedidos e a contagem retry não tiver sido alcançada, em seguida, a atividade deve ser executada novamente, mas não antes da tentativa intervalo expirou. Na Figura 6 , em vez de iniciar outra iteração diretamente, uma assinatura de timer é criada usando o intervalo configurado na atividade.

A Figura 6 criando uma inscrição de timer

if (CurrentRetryAttempt++ < RetryCount &&
    this.ExecutionStatus == ActivityExecutionStatus.Executing) {

  this.SetValue(ActivityExecutionContext.CurrentExceptionProperty, null);

  DateTime expires = DateTime.UtcNow.Add(RetryInterval);
  SubscriptionID = Guid.NewGuid();

  WorkflowQueuingService qSvc = 
    thisContext.GetService<WorkflowQueuingService>();
  WorkflowQueue q = qSvc.CreateWorkflowQueue(SubscriptionID, false);
  q.QueueItemAvailable += new EventHandler<QueueEventArgs>(TimerExpired);

  TimerEventSubscription subscription = new TimerEventSubscription(
    SubscriptionID, WorkflowInstanceId, expires);
  TimerEventSubscriptionCollection timers = 
    GetTimerSubscriptionCollection();
  timers.Add(subscription);

  return;
}

Quando o timer expira, o método TimerExpired será chamado, conforme mostrado aqui:

void TimerExpired(object sender, QueueEventArgs e)
{
  ActivityExecutionContext ctx = 
    sender as ActivityExecutionContext;
  CleanupSubscription(ctx);
  BeginIteration(ctx);
}

fig07.gif

A Figura 7 A atividade de repetição em um fluxo de trabalho

Isso iniciará a próxima iteração da atividade filho. Usando a classe TimerEventSubscription e adicionando o timer à coleção de timer do fluxo de trabalho, a atividade é capaz de corretamente participar persistência e continuidade com qualquer serviço de persistência está configurado atualmente em tempo de execução. Se o intervalo de repetição for longo, o fluxo de trabalho inteiro pode ser retirado fora da memória até que o timer expire.

O comportamento de chave da atividade do fluxo de trabalho foi atendido neste momento. Se um falhas de atividade filho, a atividade de repetição não será falha. Em vez disso, ele será pausar para o intervalo de repetição e tentar executar a atividade filho novamente.

A etapa final é lidar com o caso em que a atividade atingiu a contagem de repetição e a atividade filho continuou a falhar. Nesse caso, o método Activity_Faulting não limpa a exceção na atividade filho, como o objetivo é permitir que essa falha de atividade como normal. E quando a atividade filho fecha, também fecha a atividade de repetição.

Quando a fecha repetir novamente depois que todas as tentativas que apresentaram falha, o resultado é o mesmo como se o trabalho original tinha falhou em uma seqüência. A atividade de repetição pode ter atividades FaultHandler definidas e os manipuladores fault só serão executado depois que todas as tentativas tenham sido executadas. Usando esse modelo simplifica o desenvolvimento de fluxos de trabalho com ações que talvez precisem ser repetida, ainda mantém a mesma experiência de desenvolvimento para fluxo de trabalho desenvolvedores em relação tratamento de falhas como mostrado na Figura 7 .

Além disso, os manipuladores fault serão executados para a atividade filho quando as tentativas de repetição falharam, para que os desenvolvedores de fluxo de trabalho podem escolher tratar as falhas em qualquer atividade. O método HandleFault é chamado na atividade filho para cada falha, garantindo que a atividade tenha a oportunidade de limpar em cada iteração.

Envie suas dúvidas e comentários para mmnet30@Microsoft.com.

Matt Milner é um membro da equipe técnica na Pluralsight, onde ele se concentra em tecnologias de sistemas conectados. Matt também é um consultor independente especializado em tecnologias Microsoft .NET com um foco no Windows Workflow Foundation, o BizTalk Server, ASP.NET e o Windows Communication Foundation. Matt mora em Santa Catarina com sua esposa, Kristen e seus dois filhos. Entre em contato com Matt via seu blog em pluralsight.com/community/blogs/Matt.