Kompensation
Kompensation i Windows Workflow Foundation (WF) är den mekanism som tidigare slutfört arbete kan ångras eller kompenseras (enligt logiken som definieras av programmet) när ett efterföljande fel inträffar. I det här avsnittet beskrivs hur du använder kompensation i arbetsflöden.
Kompensation jämfört med transaktioner
Med en transaktion kan du kombinera flera åtgärder till en enda arbetsenhet. Genom att använda en transaktion kan ditt program avbryta (återställa) alla ändringar som körs inifrån transaktionen om några fel inträffar under någon del av transaktionsprocessen. Det är dock inte lämpligt att använda transaktioner om arbetet är tidskrävande. Till exempel implementeras ett reseplaneringsprogram som ett arbetsflöde. Stegen i arbetsflödet kan bestå av att boka en flygning, vänta på chefens godkännande och sedan betala för flygningen. Den här processen kan ta många dagar och det är inte praktiskt för stegen för att boka och betala för att flygresan ska delta i samma transaktion. I ett scenario som detta kan kompensation användas för att ångra bokningssteget i arbetsflödet om det uppstår ett fel senare i bearbetningen.
Kommentar
Det här avsnittet beskriver kompensation i arbetsflöden. Mer information om transaktioner i arbetsflöden finns i Transaktioner och TransactionScope. Mer information om transaktioner finns i System.Transactions och System.Transactions.Transaction.
Använda CompensableActivity
CompensableActivity är den huvudsakliga kompensationsaktiviteten i WF. Alla aktiviteter som utför arbete som kan behöva kompenseras placeras i Body en CompensableActivity. I det här exemplet placeras reservationssteget för att köpa en flygning i Body en CompensableActivity och annulleringen av reservationen CompensationHandlerplaceras i . Omedelbart efter CompensableActivity i arbetsflödet är två aktiviteter som väntar på godkännande av chefen och sedan slutföra köpsteget för flygningen. Om ett feltillstånd gör att arbetsflödet avbryts när det CompensableActivity har slutförts schemaläggs aktiviteterna CompensationHandler i hanteraren och flygresan avbryts.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new ManagerApproval(),
new PurchaseFlight()
}
};
Följande exempel är arbetsflödet i 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>
När arbetsflödet anropas visas följande utdata i konsolen.
ReserveFlight: Biljetten är reserverad.ManagerApproval: Godkännande av chef har tagits emot.PurchaseFlight: Biljetten köps.Arbetsflödet har slutförts med status: Stängd.
Kommentar
Exempelaktiviteterna i det här avsnittet, till exempel ReserveFlight
att visa deras namn och syfte för konsolen för att illustrera i vilken ordning aktiviteterna körs när kompensationen inträffar.
Standardkompensation för arbetsflöde
Om arbetsflödet avbryts körs som standard kompensationslogik för alla kompenserbara aktiviteter som har slutförts helt och inte redan har bekräftats eller kompenserats.
Kommentar
När en CompensableActivity bekräftas kan kompensationen för aktiviteten inte längre anropas. Bekräftelseprocessen beskrivs senare i det här avsnittet.
I det här exemplet utlöses ett undantag efter att flygresan har reserverats men före steget för godkännande av chef.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new SimulatedErrorCondition(),
new ManagerApproval(),
new PurchaseFlight()
}
};
Det här exemplet är arbetsflödet i 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();
När arbetsflödet anropas hanteras undantaget för simulerade felvillkor av värdprogrammet i OnUnhandledException, arbetsflödet avbryts och kompensationslogik anropas.
ReserveFlight: Biljetten är reserverad.SimulatedErrorCondition: Utlöser en ApplicationException.Arbetsflöde Ohanterat undantag:System.ApplicationException: Simulerat felvillkor i arbetsflödet.CancelFlight: Biljetten har avbrutits.Arbetsflödet har slutförts med status: Avbröts.
Annullering och compensableActivity
Om aktiviteterna Body i en CompensableActivity inte har slutförts och aktiviteten avbryts, körs aktiviteterna CancellationHandler i .
Kommentar
CancellationHandler Anropas endast om aktiviteterna i Body av CompensableActivity inte har slutförts och aktiviteten avbryts. CompensationHandler Körs endast om aktiviteterna Body i av CompensableActivity har slutförts och kompensationen sedan anropas för aktiviteten.
CancellationHandler Ger arbetsflödesförfattarna möjlighet att tillhandahålla lämplig annulleringslogik. I följande exempel utlöses ett undantag under körningen Bodyav och anropas sedan CancellationHandler .
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()
}
};
Det här exemplet är arbetsflödet i 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>
När arbetsflödet anropas hanteras undantaget för simulerade felvillkor av värdprogrammet i OnUnhandledException, arbetsflödet avbryts och annulleringslogik CompensableActivity för anropas. I det här exemplet har kompensationslogik och annulleringslogik olika mål. Om det Body har slutförts innebär det att kreditkortet debiterades och flygresan bokades, så kompensationen bör ångra båda stegen. (I det här exemplet avbryter avbokningen automatiskt kreditkortsavgifterna.) Men om den CompensableActivity avbryts innebär det att Body den inte slutfördes, så logiken CancellationHandler för de måste kunna avgöra hur du bäst hanterar annulleringen. I det här exemplet CancellationHandler avbryter kreditkortsavgiften, men eftersom ReserveFlight
var den sista aktiviteten i Bodyförsöker den inte avbryta flyget. Eftersom ReserveFlight
var den sista aktiviteten i Body, om den hade slutförts skulle Body den ha slutförts och ingen annullering skulle vara möjlig.
ChargeCreditCard: Debitera kreditkort för flygning.SimulatedErrorCondition: Utlöser en ApplicationException.Arbetsflöde Ohanterat undantag:System.ApplicationException: Simulerat felvillkor i arbetsflödet.CancelCreditCard: Avbryt kreditkortsavgifter.Arbetsflödet har slutförts med status: Avbröts. Mer information om annullering finns i Annullering.
Explicit kompensation med hjälp av kompensationsaktiviteten
I föregående avsnitt omfattades implicit kompensation. Implicit kompensation kan vara lämplig för enkla scenarier, men om det krävs mer explicit kontroll över schemaläggningen av kompensationshantering Compensate kan aktiviteten användas. För att initiera kompensationsprocessen med Compensate aktiviteten används den CompensationTokenCompensableActivity för vilken kompensation önskas. Aktiviteten Compensate kan användas för att initiera ersättning för alla slutförda CompensableActivity som inte har bekräftats eller kompenserats. En aktivitet kan till exempel Compensate användas i Catches avsnittet i en TryCatch aktivitet eller när som helst efter att den CompensableActivity har slutförts. I det här exemplet Compensate används aktiviteten i Catches avsnittet i en TryCatch aktivitet för att ångra åtgärden för 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
}
}
}
}
};
Det här exemplet är arbetsflödet i 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>
När arbetsflödet anropas visas följande utdata i konsolen.
ReserveFlight: Biljetten är reserverad.SimulatedErrorCondition: Utlöser en ApplicationException.CancelFlight: Biljetten har avbrutits.Arbetsflödet har slutförts med status: Stängd.
Bekräfta kompensation
Som standard kan kompenserbara aktiviteter kompenseras när som helst när de har slutförts. I vissa scenarier kanske detta inte är lämpligt. I föregående exempel var kompensationen för att reservera biljetten att avbryta reservationen. Men efter att flygningen har slutförts är detta kompensationssteg inte längre giltigt. Om du bekräftar den kompenserbara aktiviteten anropas aktiviteten som anges av ConfirmationHandler. En möjlig användning för detta är att tillåta att alla resurser som krävs för att utföra kompensationen frisläpps. När en kompenserbar aktivitet har bekräftats är det inte möjligt att kompensera den, och om detta försök görs genereras ett InvalidOperationException undantag. När ett arbetsflöde har slutförts bekräftas alla icke-bekräftade och icke-kompenserade kompenserbara aktiviteter som har slutförts i omvänd ordning. I det här exemplet är flygningen reserverad, köpt och slutförd, och sedan bekräftas den kompenserbara aktiviteten. Om du vill bekräfta en CompensableActivityanvänder du Confirm aktiviteten och anger CompensationToken för CompensableActivity att bekräfta.
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
}
}
};
Det här exemplet är arbetsflödet i 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>
När arbetsflödet anropas visas följande utdata i konsolen.
ReserveFlight: Biljetten är reserverad.ManagerApproval: Godkännande av chef har tagits emot.PurchaseFlight: Biljetten köps.TakeFlight: Flight har slutförts.ConfirmFlight: Flyg har tagits, ingen ersättning är möjlig.Arbetsflödet har slutförts med status: Stängd.
Kapsling av kompensationsaktiviteter
A CompensableActivity kan placeras i avsnittet i Body en annan CompensableActivity. A CompensableActivity får inte placeras i en hanterare för en annan CompensableActivity. Det är en förälders CompensableActivity ansvar att se till att när den avbryts, bekräftas eller kompenseras måste alla underordnade kompenserbara aktiviteter som har slutförts framgångsrikt och inte redan har bekräftats eller kompenserats bekräftas eller kompenseras innan föräldern slutför annulleringen, bekräftelsen eller ersättningen. Om detta inte uttryckligen modelleras kompenserar den överordnade CompensableActivity implicit underordnade kompenserbara aktiviteter om den överordnade har fått avboknings- eller kompensationssignalen. Om den överordnade har fått bekräftelsesignalen bekräftar den överordnade implicit underordnade kompenserbara aktiviteterna. Om logiken för att hantera annullering, bekräftelse eller kompensation uttryckligen modelleras i hanteraren för den överordnade CompensableActivity, bekräftas alla underordnade som inte hanteras uttryckligen.