Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
A compensação no Windows Workflow Foundation (WF) é o mecanismo pelo qual o trabalho concluído anteriormente pode ser desfeito ou compensado (seguindo a lógica definida pelo aplicativo) quando ocorre uma falha subsequente. Esta seção descreve como usar a compensação em fluxos de trabalho.
Compensação vs. Transações
Uma transação permite combinar várias operações em uma única unidade de trabalho. O uso de uma transação dá ao seu aplicativo a capacidade de anular (reverter) todas as alterações executadas a partir da transação se ocorrerem erros durante qualquer parte do processo de transação. No entanto, o uso de transações pode não ser apropriado se o trabalho for de longa duração. Por exemplo, um aplicativo de planejamento de viagens é implementado como um fluxo de trabalho. As etapas do fluxo de trabalho podem consistir em reservar um voo, aguardar a aprovação do gerente e, em seguida, pagar pelo voo. Este processo pode demorar muitos dias e não é prático que as etapas de reserva e pagamento do voo participem na mesma transação. Em um cenário como este, a compensação pode ser usada para desfazer a etapa de reserva do fluxo de trabalho se houver uma falha posteriormente no processamento.
Observação
Este tópico aborda a remuneração em fluxos de trabalho. Para obter mais informações sobre transações em fluxos de trabalho, consulte Transactions and TransactionScope. Para obter mais informações sobre transações, consulte System.Transactions e System.Transactions.Transaction.
Usando Atividade Compensável
CompensableActivity é a atividade central de compensação em WF. Quaisquer atividades que realizem trabalho que poderá precisar ser compensado são colocadas na Body de um CompensableActivity. Neste exemplo, a etapa de reserva da compra de um voo é colocada na Body de um CompensableActivity e o cancelamento da reserva é colocado no CompensationHandler. Imediatamente após a CompensableActivity no fluxo de trabalho estão duas atividades que aguardam a aprovação do gerente e, em seguida, concluem a etapa de compra do voo. Se uma condição de erro fizer com que o fluxo de trabalho seja cancelado após o CompensableActivity ter sido concluído com sucesso, as atividades no manipulador de CompensationHandler serão agendadas e o voo será cancelado.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new ManagerApproval(),
new PurchaseFlight()
}
};
O exemplo a seguir é o fluxo de trabalho em XAML.
<Sequence
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CompensableActivity>
<CompensableActivity.Result>
<OutArgument
x:TypeArguments="CompensationToken" />
</CompensableActivity.Result>
<CompensableActivity.CompensationHandler>
<c:CancelFlight />
</CompensableActivity.CompensationHandler>
<c:ReserveFlight />
</CompensableActivity>
<c:ManagerApproval />
<c:PurchaseFlight />
</Sequence>
Quando o fluxo de trabalho é invocado, a saída a seguir é exibida no console.
ReserveFlight: O bilhete está reservado.ManagerApproval: Aprovação do gerente recebida.PurchaseFlight: O bilhete foi comprado.Fluxo de trabalho concluído com êxito com status: Fechado.
Observação
As atividades de exemplo neste tópico, como ReserveFlight
exibem seu nome e finalidade no console para ajudar a ilustrar a ordem em que as atividades são executadas quando ocorre a compensação.
Compensação de fluxo de trabalho padrão
Por padrão, se o fluxo de trabalho for cancelado, a lógica de compensação será executada para qualquer atividade compensável que tenha sido concluída com êxito e ainda não tenha sido confirmada ou compensada.
Observação
Quando um CompensableActivity é confirmado , a compensação pela atividade não pode mais ser invocada. O processo de confirmação é descrito mais adiante nesta seção.
Neste exemplo, uma exceção é lançada depois de o voo ser reservado, mas antes do passo de aprovação do gestor.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new SimulatedErrorCondition(),
new ManagerApproval(),
new PurchaseFlight()
}
};
Este exemplo é o fluxo de trabalho em XAML.
<Sequence
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CompensableActivity>
<CompensableActivity.Result>
<OutArgument
x:TypeArguments="CompensationToken" />
</CompensableActivity.Result>
<CompensableActivity.CompensationHandler>
<c:CancelFlight />
</CompensableActivity.CompensationHandler>
<c:ReserveFlight />
</CompensableActivity>
<c:SimulatedErrorCondition />
<c:ManagerApproval />
<c:PurchaseFlight />
</Sequence>
AutoResetEvent syncEvent = new AutoResetEvent(false);
WorkflowApplication wfApp = new WorkflowApplication(wf);
wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
if (e.TerminationException != null)
{
Console.WriteLine($"""
Workflow terminated with exception:
{e.TerminationException.GetType().FullName}: {e.TerminationException.Message}
""");
}
else
{
Console.WriteLine($"Workflow completed successfully with status: {e.CompletionState}.");
}
syncEvent.Set();
};
wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
Console.WriteLine($"""
Workflow Unhandled Exception:
{e.UnhandledException.GetType().FullName}: {e.UnhandledException.Message}
""");
return UnhandledExceptionAction.Cancel;
};
wfApp.Run();
syncEvent.WaitOne();
Quando o fluxo de trabalho é invocado, a exceção de condição de erro simulada é manipulada pelo aplicativo host em OnUnhandledException, o fluxo de trabalho é cancelado e a lógica de compensação é invocada.
ReserveFlight: O bilhete está reservado.SimulatedErrorCondition: lançando um ApplicationException.Exceção não tratada do fluxo de trabalho:System.ApplicationException: condição de erro simulada no fluxo de trabalho.CancelFlight: O bilhete é cancelado.Fluxo de trabalho concluído com êxito com status: Cancelado.
Cancelamento e Atividade Compensatória
Se as atividades no Body de um CompensableActivity não tiverem sido concluídas e a atividade for cancelada, as atividades no CancellationHandler serão executadas.
Observação
O CancellationHandler só é invocado se as atividades no Body do CompensableActivity não tiverem sido concluídas e a atividade for cancelada. A CompensationHandler só é executada se as atividades no Body do CompensableActivity tiverem sido concluídas com êxito e se for posteriormente invocada uma compensação sobre a atividade.
O CancellationHandler dá aos autores do fluxo de trabalho a oportunidade de fornecer qualquer lógica de cancelamento apropriada. No exemplo a seguir, uma exceção é lançada durante a execução do Bodye, em seguida, o CancellationHandler é invocado.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new Sequence
{
Activities =
{
new ChargeCreditCard(),
new SimulatedErrorCondition(),
new ReserveFlight()
}
},
CompensationHandler = new CancelFlight(),
CancellationHandler = new CancelCreditCard()
},
new ManagerApproval(),
new PurchaseFlight()
}
};
Este exemplo é o fluxo de trabalho em XAML
<Sequence
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<CompensableActivity>
<CompensableActivity.Result>
<OutArgument
x:TypeArguments="CompensationToken" />
</CompensableActivity.Result>
<Sequence>
<c:ChargeCreditCard />
<c:SimulatedErrorCondition />
<c:ReserveFlight />
</Sequence>
<CompensableActivity.CancellationHandler>
<c:CancelCreditCard />
</CompensableActivity.CancellationHandler>
<CompensableActivity.CompensationHandler>
<c:CancelFlight />
</CompensableActivity.CompensationHandler>
</CompensableActivity>
<c:ManagerApproval />
<c:PurchaseFlight />
</Sequence>
Quando o fluxo de trabalho é invocado, a exceção de condição de erro simulada é manipulada pelo aplicativo host em OnUnhandledException, o fluxo de trabalho é cancelado e a lógica de cancelamento do CompensableActivity é invocada. Neste exemplo, a lógica de compensação e a lógica de cancelamento têm objetivos diferentes. Se o Body foi concluído com sucesso, isso significa que o cartão de crédito foi cobrado e o voo reservado, então a compensação deve anular ambos os passos. (Neste exemplo, cancelar o voo cancela automaticamente as cobranças do cartão de crédito.) No entanto, se o CompensableActivity for cancelado, isso significa que o Body não foi concluído e, portanto, a lógica do CancellationHandler precisa ser capaz de determinar a melhor forma de lidar com o cancelamento. Neste exemplo, o CancellationHandler cancela a cobrança do cartão de crédito, mas como ReserveFlight
foi a última atividade no Body, não tenta cancelar o voo. Uma vez que ReserveFlight
foi a última atividade no Body, se tivesse sido concluída com sucesso, a Body teria sido concluída e nenhum cancelamento seria possível.
ChargeCreditCard: Cobra cartão de crédito pelo voo.SimulatedErrorCondition: lançando um ApplicationException.Exceção não tratada do fluxo de trabalho:System.ApplicationException: condição de erro simulada no fluxo de trabalho.CancelCreditCard: Cancele cobranças de cartão de crédito.Fluxo de trabalho concluído com êxito com status: Cancelado. Para obter mais informações sobre cancelamento, consulte Cancelamento.
Compensação explícita usando a atividade de compensação
Na secção anterior, a indemnização implícita foi abrangida. A compensação implícita pode ser apropriada para cenários simples, mas se for necessário um controle mais explícito sobre o agendamento do tratamento da compensação, a atividade Compensate pode ser usada. Para iniciar o processo de compensação com a atividade Compensate, utiliza-se o CompensationToken do CompensableActivity para o qual a compensação é desejada. A atividade Compensate pode ser usada para iniciar uma compensação em qualquer atividade CompensableActivity concluída que não tenha sido confirmada ou compensada. Por exemplo, uma atividade Compensate pode ser usada na seção Catches de uma atividade TryCatch ou a qualquer momento após a conclusão do CompensableActivity. Neste exemplo, a atividade Compensate é usada na seção Catches de uma atividade TryCatch para reverter a ação do CompensableActivity.
Variable<CompensationToken> token1 = new Variable<CompensationToken>
{
Name = "token1",
};
Activity wf = new TryCatch()
{
Variables =
{
token1
},
Try = new Sequence
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight(),
ConfirmationHandler = new ConfirmFlight(),
Result = token1
},
new SimulatedErrorCondition(),
new ManagerApproval(),
new PurchaseFlight()
}
},
Catches =
{
new Catch<ApplicationException>()
{
Action = new ActivityAction<ApplicationException>()
{
Handler = new Compensate()
{
Target = token1
}
}
}
}
};
Este exemplo é o fluxo de trabalho em XAML.
<TryCatch
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
xmlns:s="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TryCatch.Variables>
<Variable
x:TypeArguments="CompensationToken"
x:Name="__ReferenceID0"
Name="token1" />
</TryCatch.Variables>
<TryCatch.Try>
<Sequence>
<CompensableActivity>
<CompensableActivity.Result>
<OutArgument
x:TypeArguments="CompensationToken">
<VariableReference
x:TypeArguments="CompensationToken"
Variable="{x:Reference __ReferenceID0}">
<VariableReference.Result>
<OutArgument
x:TypeArguments="Location(CompensationToken)" />
</VariableReference.Result>
</VariableReference>
</OutArgument>
</CompensableActivity.Result>
<CompensableActivity.CompensationHandler>
<c:CancelFlight />
</CompensableActivity.CompensationHandler>
<CompensableActivity.ConfirmationHandler>
<c:ConfirmFlight />
</CompensableActivity.ConfirmationHandler>
<c:ReserveFlight />
</CompensableActivity>
<c:SimulatedErrorCondition />
<c:ManagerApproval />
<c:PurchaseFlight />
</Sequence>
</TryCatch.Try>
<TryCatch.Catches>
<Catch
x:TypeArguments="s:ApplicationException">
<ActivityAction
x:TypeArguments="s:ApplicationException">
<Compensate>
<Compensate.Target>
<InArgument
x:TypeArguments="CompensationToken">
<VariableValue
x:TypeArguments="CompensationToken"
Variable="{x:Reference __ReferenceID0}">
<VariableValue.Result>
<OutArgument
x:TypeArguments="CompensationToken" />
</VariableValue.Result>
</VariableValue>
</InArgument>
</Compensate.Target>
</Compensate>
</ActivityAction>
</Catch>
</TryCatch.Catches>
</TryCatch>
Quando o fluxo de trabalho é invocado, a saída a seguir é exibida no console.
ReserveFlight: O bilhete está reservado.SimulatedErrorCondition: lançando um ApplicationException.CancelFlight: O bilhete foi cancelado.Fluxo de trabalho concluído com êxito com status: Fechado.
Confirmação da Compensação
Por padrão, as atividades compensáveis podem ser compensadas a qualquer momento após a sua conclusão. Em alguns cenários, isso pode não ser apropriado. No exemplo anterior, a compensação para reservar o bilhete era cancelar a reserva. No entanto, após a conclusão do voo, esta etapa de compensação deixa de ser válida. A confirmação da atividade compensável invoca a atividade especificada pelo ConfirmationHandler. Um uso possível para isso é permitir que todos os recursos necessários para realizar a compensação sejam liberados. Depois que uma atividade compensável é confirmada, não é possível que ela seja compensada e, se isso for tentado, uma exceção InvalidOperationException é lançada. Quando um fluxo de trabalho é concluído com êxito, todas as atividades compensáveis não confirmadas e não compensadas concluídas com êxito são confirmadas na ordem inversa de conclusão. Neste exemplo, o voo é reservado, comprado e concluído e, em seguida, a atividade compensável é confirmada. Para confirmar um CompensableActivity, utilize a atividade Confirm e especifique o CompensationToken do CompensableActivity a confirmar.
Variable<CompensationToken> token1 = new Variable<CompensationToken>
{
Name = "token1",
};
Activity wf = new Sequence()
{
Variables =
{
token1
},
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight(),
ConfirmationHandler = new ConfirmFlight(),
Result = token1
},
new ManagerApproval(),
new PurchaseFlight(),
new TakeFlight(),
new Confirm()
{
Target = token1
}
}
};
Este exemplo é o fluxo de trabalho em XAML.
<Sequence
xmlns="http://schemas.microsoft.com/netfx/2009/xaml/activities"
xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Sequence.Variables>
<x:Reference>__ReferenceID0</x:Reference>
</Sequence.Variables>
<CompensableActivity>
<CompensableActivity.Result>
<OutArgument
x:TypeArguments="CompensationToken">
<VariableReference
x:TypeArguments="CompensationToken">
<VariableReference.Result>
<OutArgument
x:TypeArguments="Location(CompensationToken)" />
</VariableReference.Result>
<VariableReference.Variable>
<Variable
x:TypeArguments="CompensationToken"
x:Name="__ReferenceID0"
Name="token1" />
</VariableReference.Variable>
</VariableReference>
</OutArgument>
</CompensableActivity.Result>
<CompensableActivity.CompensationHandler>
<c:CancelFlight />
</CompensableActivity.CompensationHandler>
<CompensableActivity.ConfirmationHandler>
<c:ConfirmFlight />
</CompensableActivity.ConfirmationHandler>
<c:ReserveFlight />
</CompensableActivity>
<c:ManagerApproval />
<c:PurchaseFlight />
<c:TakeFlight />
<Confirm>
<Confirm.Target>
<InArgument
x:TypeArguments="CompensationToken">
<VariableValue
x:TypeArguments="CompensationToken"
Variable="{x:Reference __ReferenceID0}">
<VariableValue.Result>
<OutArgument
x:TypeArguments="CompensationToken" />
</VariableValue.Result>
</VariableValue>
</InArgument>
</Confirm.Target>
</Confirm>
</Sequence>
Quando o fluxo de trabalho é invocado, a saída a seguir é exibida no console.
ReserveFlight: O bilhete está reservado.ManagerApproval: aprovação do gerente recebida.PurchaseFlight: O bilhete é comprado.TakeFlight: O voo está concluído.ConfirmFlight: O voo foi realizado, sem possibilidade de compensação.Fluxo de trabalho concluído com êxito com status: Fechado.
Atividades de Compensação de Nidificação
Um CompensableActivity pode ser colocado na secção Body de outro CompensableActivity. Um CompensableActivity não pode ser colocado num manipulador de outro CompensableActivity. É da responsabilidade de um progenitor CompensableActivity garantir que, quando for cancelada, confirmada ou compensada, todas as atividades indemnizáveis da criança que tenham sido concluídas com sucesso e que ainda não tenham sido confirmadas ou compensadas devem ser confirmadas ou compensadas antes de o progenitor concluir o cancelamento, confirmação ou compensação. Se isso não for modelado explicitamente, o CompensableActivity pai compensará implicitamente as atividades compensáveis do filho se o pai receber o sinal de cancelamento ou compensação. Se o pai recebeu o sinal de confirmação, o pai confirmará implicitamente as atividades compensáveis da criança. Se a lógica para lidar com cancelamento, confirmação ou compensação for explicitamente modelada no manipulador do elemento pai CompensableActivity, qualquer elemento filho não manipulado explicitamente será implicitamente confirmado.