Kompenzace
Kompenzace ve Windows Workflow Foundation (WF) je mechanismus, kterým lze dříve dokončenou práci vrátit zpět nebo kompenzovat (podle logiky definované aplikací), když dojde k následné chybě. Tato část popisuje, jak používat kompenzaci v pracovních postupech.
Kompenzace vs. transakce
Transakce umožňuje kombinovat více operací do jedné jednotky práce. Použití transakce dává vaší aplikaci možnost přerušit (vrátit zpět) všechny změny provedené z transakce, pokud dojde k nějakým chybám během libovolné části procesu transakce. Použití transakcí však nemusí být vhodné, pokud je práce dlouhotrvající. Aplikace pro plánování cest je například implementována jako pracovní postup. Kroky pracovního postupu se můžou skládat z rezervace letu, čekání na schválení manažera a následné platby za let. Tento proces může trvat mnoho dní a není praktické provést kroky rezervace a platit za let, aby se zúčastnil stejné transakce. V takovém případě je možné kompenzaci použít k vrácení kroku rezervace pracovního postupu zpět, pokud dojde k selhání později při zpracování.
Poznámka:
Toto téma se zabývá kompenzací v pracovních postupech. Další informace o transakcích v pracovních postupech naleznete v tématu Transakce a TransactionScope. Další informace o transakcích naleznete System.Transactions a System.Transactions.Transaction.
Používání funkce CompensableActivity
CompensableActivity je základní kompenzační aktivita ve WF. Všechny činnosti, které provádějí práci, které mohou být nutné kompenzovat, jsou umístěny do BodyCompensableActivity V tomto příkladu je krok rezervace nákupu letu umístěn do BodyCompensableActivity a zrušení rezervace se umístí do CompensationHandler. Bezprostředně za krokem CompensableActivity v pracovním postupu jsou dvě aktivity, které čekají na schválení manažera a pak dokončí nákupní krok testovací verze. Pokud po úspěšném dokončení pracovního postupu dojde k CompensableActivity chybě, naplánují se aktivity v CompensationHandler obslužné rutině a testovací verze se zruší.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new ManagerApproval(),
new PurchaseFlight()
}
};
Následující příklad je pracovní postup v JAZYCE 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>
Při vyvolání pracovního postupu se v konzole zobrazí následující výstup.
ReserveFlight: Rezervace vstupenek je rezervovaná.ManagerApproval: Bylo přijato schválení manažerem.PurchaseFlight: Letenka je zakoupena.Pracovní postup byl úspěšně dokončen se stavem: Uzavřeno.
Poznámka:
Ukázkové aktivity v tomto tématu, jako ReserveFlight
je zobrazení jejich názvu a účelu v konzole, aby pomohly ilustrovat pořadí, ve kterém se aktivity provádějí, když dojde k kompenzaci.
Výchozí kompenzace pracovního postupu
Ve výchozím nastavení platí, že pokud je pracovní postup zrušen, logika kompenzace se spustí pro jakoukoli kompenzní aktivitu, která byla úspěšně dokončena a ještě nebyla potvrzena nebo kompenzována.
Poznámka:
CompensableActivity Po potvrzení se už nedá vyvolat kompenzace aktivity. Proces potvrzení je popsán dále v této části.
V tomto příkladu se po rezervaci letu vyvolá výjimka, ale před krokem schválení manažera.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new SimulatedErrorCondition(),
new ManagerApproval(),
new PurchaseFlight()
}
};
Tento příklad je pracovní postup v 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:\n{0}: {1}",
e.TerminationException.GetType().FullName,
e.TerminationException.Message);
}
else
{
Console.WriteLine("Workflow completed successfully with status: {0}.",
e.CompletionState);
}
syncEvent.Set();
};
wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
Console.WriteLine("Workflow Unhandled Exception:\n{0}: {1}",
e.UnhandledException.GetType().FullName,
e.UnhandledException.Message);
return UnhandledExceptionAction.Cancel;
};
wfApp.Run();
syncEvent.WaitOne();
Při vyvolání pracovního postupu se simulovaná výjimka chybového stavu zpracuje hostitelskou aplikací v OnUnhandledException, pracovní postup se zruší a vyvolá se logika kompenzace.
ReserveFlight: Rezervace vstupenek je rezervovaná.SimulatedErrorCondition: Vyvolání výjimky ApplicationException.Neošetřená výjimka:System.ApplicationException: Simulovaný chybový stav v pracovním postupu.CancelFlight: Rezervace se zruší.Pracovní postup byl úspěšně dokončen se stavem: Zrušeno.
Zrušení a compensableActivity
Pokud aktivity v objektu BodyCompensableActivity nejsou dokončeny a aktivita je zrušena, aktivity v dané aktivitě CancellationHandler se spustí.
Poznámka:
Vyvolá CancellationHandler se pouze v případě, že aktivity v objektu BodyCompensableActivity nebyly dokončeny a aktivita je zrušena. Provede CompensationHandler se pouze v případě, že se aktivity v BodyCompensableActivity činnosti úspěšně dokončily a následně se na aktivitu vyvolá kompenzace.
Poskytuje CancellationHandler autorům pracovního postupu příležitost poskytnout jakoukoli odpovídající logiku zrušení. V následujícím příkladu je vyvolána výjimka během provádění objektu Body, a poté je CancellationHandler vyvolána.
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()
}
};
Tento příklad je pracovní postup v 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>
Při vyvolání pracovního postupu se simulovaná výjimka chybového stavu zpracuje hostitelskou aplikací v OnUnhandledException, pracovní postup se zruší a vyvolá se logika CompensableActivity zrušení. V tomto příkladu má logika kompenzace a logika zrušení různé cíle. Pokud se Body úspěšně dokončilo, znamená to, že byla platební karta účtována a let rezervován, takže kompenzace by měla vrátit zpět oba kroky. (V tomto příkladu zrušení testovací verze automaticky zruší poplatky za platební kartu.) Pokud CompensableActivity je však zrušena, znamená to, že Body se nedokončilo, a proto CancellationHandler je potřeba zjistit, jak nejlépe zpracovat zrušení. V tomto příkladu CancellationHandler zruší poplatky za platební kartu, ale od ReserveFlight
poslední aktivity v Bodytomto příkladu se nepokoušá o zrušení letu. Vzhledem k tomu ReserveFlight
, že byla poslední aktivita v nástroji Body, pokud byla úspěšně dokončena, Body byla by dokončena a zrušení by nebylo možné.
ChargeCreditCard: Platba platební karty za let.SimulatedErrorCondition: Vyvolání výjimky ApplicationException.Neošetřená výjimka:System.ApplicationException: Simulovaný chybový stav v pracovním postupu.CancelCreditCard: Zrušení poplatků za platební kartuPracovní postup byl úspěšně dokončen se stavem: Zrušeno. Další informace o zrušení naleznete v tématu Zrušení.
Explicitní kompenzace pomocí aktivity kompenzace
V předchozí části byla pokryta implicitní kompenzace. Implicitní kompenzace může být vhodná pro jednoduché scénáře, ale pokud je potřeba explicitnější kontrolu nad plánováním zpracování kompenzací, Compensate lze aktivitu použít. Chcete-li zahájit proces kompenzace s aktivitouCompensate, CompensableActivityCompensationToken použije se požadovaná kompenzace. Aktivitu Compensate lze použít k zahájení kompenzace u všech dokončených CompensableActivity , které nebyly potvrzeny nebo kompenzovány. Aktivitu můžete například Compensate použít v Catches části TryCatch aktivity nebo kdykoli po CompensableActivity dokončení. V tomto příkladu se Compensate aktivita používá v Catches části TryCatch aktivity k obrácení akce objektu 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
}
}
}
}
};
Tento příklad je pracovní postup v 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>
Při vyvolání pracovního postupu se v konzole zobrazí následující výstup.
ReserveFlight: Rezervace vstupenek je rezervovaná.SimulatedErrorCondition: Vyvolání výjimky ApplicationException.CancelFlight: Rezervace se zruší.Pracovní postup byl úspěšně dokončen se stavem: Uzavřeno.
Potvrzení kompenzace
Ve výchozím nastavení je možné kompenzuje aktivity kdykoli po dokončení. V některých scénářích to nemusí být vhodné. V předchozím příkladu byla platba za rezervaci lístku zrušena. Po dokončení letu však tento krok kompenzace již není platný. Potvrzením, že kompensovatelná aktivita vyvolá aktivitu určenou parametrem ConfirmationHandler. Jedním zmožnýchch Poté, co je compensable aktivita potvrzena, že není možné ji kompenzovat, a pokud se pokusí o InvalidOperationException výjimku je vyvolána. Po úspěšném dokončení pracovního postupu jsou všechny neověřené a nekompenzované aktivity, které byly úspěšně dokončeny, potvrzeny v opačném pořadí dokončení. V tomto příkladu je let rezervovaný, zakoupený a dokončený a pak je potvrzena kompensovatelná aktivita. Chcete-li potvrdit CompensableActivity, použijte Confirm aktivitu a zadejte CompensationToken potvrzení CompensableActivity .
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
}
}
};
Tento příklad je pracovní postup v 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>
Při vyvolání pracovního postupu se v konzole zobrazí následující výstup.
ReserveFlight: Rezervace vstupenek je rezervovaná.ManagerApproval: Bylo přijato schválení manažerem.PurchaseFlight: Letenka je zakoupena.TakeFlight: Let je dokončen.ConfirmFlight: Let byl přijat, není možné žádné kompenzace.Pracovní postup byl úspěšně dokončen se stavem: Uzavřeno.
Vnoření kompenzačních aktivit
A CompensableActivity lze umístit do Body oddílu jiného CompensableActivity. Nelze CompensableActivity umístit do obslužné rutiny jiného CompensableActivity. Je odpovědností rodiče CompensableActivity , aby se zajistilo, že při zrušení, potvrzení nebo kompenzaci se všechny aktivity, které byly úspěšně dokončeny, a které ještě nebyly potvrzeny nebo vykompenzovány, musí být potvrzeny nebo vykompenzovány před ukončením, potvrzením nebo kompenzací rodiče. Pokud není modelován explicitně, nadřazený objekt CompensableActivity implicitně kompenzuje podřízené compensable aktivity, pokud nadřazený objekt obdržel signál zrušení nebo kompenzace. Pokud nadřazený objekt obdržel potvrzovací signál, nadřazený objekt implicitně potvrdí podřízené aktivity, které je možné zajistit. Pokud je logika pro zpracování zrušení, potvrzení nebo kompenzace explicitně modelována v obslužné rutině nadřazeného CompensableActivityobjektu , všechny podřízené položky, které nejsou explicitně zpracovány, se implicitně potvrdí.