Compensatie
Compensatie in Windows Workflow Foundation (WF) is het mechanisme waarmee eerder voltooid werk ongedaan kan worden gemaakt of gecompenseerd (volgens de logica die door de toepassing is gedefinieerd) wanneer er een volgende fout optreedt. In deze sectie wordt beschreven hoe u compensatie gebruikt in werkstromen.
Compensatie versus transacties
Met een transactie kunt u meerdere bewerkingen combineren tot één werkeenheid. Als u een transactie gebruikt, heeft uw toepassing de mogelijkheid om alle wijzigingen die vanuit de transactie worden uitgevoerd, af te breken (terug te draaien) als er fouten optreden tijdens een deel van het transactieproces. Het gebruik van transacties is echter mogelijk niet geschikt als het werk lang duurt. Een toepassing voor het plannen van reizen wordt bijvoorbeeld geïmplementeerd als een werkstroom. De stappen van de werkstroom kunnen bestaan uit het boeken van een vlucht, wachten op goedkeuring van de manager en vervolgens betalen voor de vlucht. Dit proces kan vele dagen duren en het is niet praktisch voor de stappen van het boeken en betalen voor de vlucht om deel te nemen aan dezelfde transactie. In een scenario zoals dit kan compensatie worden gebruikt om de boekingsstap van de werkstroom ongedaan te maken als er later een fout optreedt in de verwerking.
Notitie
In dit onderwerp wordt aandacht besteed aan compensatie in werkstromen. Zie Transacties en TransactionScopevoor meer informatie over transacties in werkstromen. Zie System.Transactions en System.Transactions.Transactionvoor meer informatie over transacties.
Het gebruik van CompensableActivity
CompensableActivity is de kerncompensatieactiviteit in WF. Activiteiten die werkzaamheden uitvoeren die mogelijk moeten worden gecompenseerd, worden in de Body van een CompensableActivitygeplaatst. In dit voorbeeld wordt de reserveringsstap voor het kopen van een vlucht in de Body van een CompensableActivity geplaatst en wordt de annulering van de reservering in de CompensationHandlergeplaatst. Direct na de CompensableActivity in de werkstroom volgen twee activiteiten die wachten op goedkeuring van de manager en vervolgens de aankoopstap van de vlucht afronden. Als een foutvoorwaarde ervoor zorgt dat de werkstroom wordt geannuleerd nadat de CompensableActivity succesvol is voltooid, worden de activiteiten in de CompensationHandler-verwerker gepland en wordt de vlucht geannuleerd.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new ManagerApproval(),
new PurchaseFlight()
}
};
Het volgende voorbeeld is de werkstroom in 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>
Wanneer de werkstroom wordt aangeroepen, wordt de volgende uitvoer weergegeven in de console.
ReserveFlight: Ticket is gereserveerd.ManagerApproval: Goedkeuring van manager ontvangen.PurchaseFlight: Ticket is gekocht.Werkstroom is voltooid met de status: Gesloten.
Notitie
De voorbeeldactiviteiten in dit onderwerp, zoals ReserveFlight
hun naam en doel aan de console weergeven om de volgorde te illustreren waarin de activiteiten worden uitgevoerd wanneer er compensatie plaatsvindt.
Standaardwerkstroomcompensatie
Als de werkstroom is geannuleerd, wordt de compensatielogica standaard uitgevoerd voor alle compenserende activiteiten die volledig zijn voltooid en die nog niet zijn bevestigd of gecompenseerd.
Notitie
Wanneer een CompensableActivity is bevestigd, kan de vergoeding voor de activiteit niet meer worden opgeroepen. Het bevestigingsproces wordt verderop in deze sectie beschreven.
In dit voorbeeld wordt er een uitzondering gegenereerd nadat de vlucht is gereserveerd, maar vóór de goedkeuringsstap van de manager.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new SimulatedErrorCondition(),
new ManagerApproval(),
new PurchaseFlight()
}
};
Dit voorbeeld is de werkstroom in 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: {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();
Wanneer de werkstroom wordt aangeroepen, wordt de uitzondering voor de gesimuleerde foutvoorwaarde verwerkt door de hosttoepassing in OnUnhandledException, wordt de werkstroom geannuleerd en wordt de compensatielogica aangeroepen.
ReserveFlight: Ticket is gereserveerd.SimulatedErrorCondition: Een ApplicationException werpen.Niet-verwerkte Uitzondering voor Werkstroom:System.ApplicationException: Gesimuleerde foutvoorwaarde in de werkstroom.CancelFlight: Ticket is geannuleerd.Werkstroom voltooid succesvol met status: Geannuleerd.
Annulering en CompenseerbareActiviteit
Als de activiteiten in de Body van een CompensableActivity niet zijn voltooid en de activiteit wordt geannuleerd, worden de activiteiten in de CancellationHandler uitgevoerd.
Notitie
De CancellationHandler wordt alleen aangeroepen als de activiteiten in de Body van de CompensableActivity niet zijn voltooid en de activiteit wordt geannuleerd. De CompensationHandler wordt alleen uitgevoerd als de activiteiten in de Body van de CompensableActivity succesvol zijn voltooid en vervolgens compensatie op de activiteit wordt aangeroepen.
Het CancellationHandler biedt werkstroomauteurs de mogelijkheid om alle juiste annuleringslogica te bieden. In het volgende voorbeeld wordt er een uitzondering gegenereerd tijdens de uitvoering van de Bodyen wordt vervolgens de CancellationHandler aangeroepen.
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()
}
};
Dit voorbeeld is de werkstroom in 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>
Wanneer de werkstroom wordt aangeroepen, wordt de uitzondering voor de gesimuleerde foutvoorwaarde verwerkt door de hosttoepassing in OnUnhandledException, wordt de werkstroom geannuleerd en wordt de annuleringslogica van de CompensableActivity aangeroepen. In dit voorbeeld hebben de compensatielogica en de annuleringslogica verschillende doelen. Als de Body succesvol voltooid is, betekent dit dat de creditcard in rekening is gebracht en de vlucht is geboekt, zodat de compensatie beide stappen ongedaan moet maken. (Als u de vlucht annuleert, worden de creditcardkosten automatisch geannuleerd.) Als de CompensableActivity echter wordt geannuleerd, betekent dit dat de Body niet is voltooid en dat de logica van de CancellationHandler dus moet kunnen bepalen hoe de annulering het beste kan worden afgehandeld. In dit voorbeeld annuleert de CancellationHandler de creditcardkosten, maar omdat ReserveFlight
de laatste activiteit in de Bodywas, wordt niet geprobeerd de vlucht te annuleren. Aangezien ReserveFlight
de laatste activiteit in de Bodywas, zou de Body zijn voltooid en zou er geen annulering mogelijk zijn.
nl-NL: ChargeCreditCard: Creditcard belasten voor vlucht.SimulatedErrorCondition: Een ApplicationException genereren.Niet-verwerkte uitzondering voor werkstroom:System.ApplicationException: Gesimuleerde foutvoorwaarde in de werkstroom.CancelCreditCard: Creditcardkosten annuleren.Werkstroom is voltooid met de status: Geannuleerd. Zie Annuleringvoor meer informatie over annulering.
Expliciete compensatie met behulp van de compensatieactiviteit
In de vorige sectie werd impliciete compensatie behandeld. Impliciete compensatie kan geschikt zijn voor eenvoudige scenario's, maar als er meer expliciete controle nodig is over de planning van compensatieafhandeling, kan de Compensate activiteit worden gebruikt. Om het compensatieproces te starten met de Compensate activiteit, wordt de CompensationToken van de CompensableActivity waarvoor compensatie gewenst is, gebruikt. De Compensate activiteit kan worden gebruikt om compensatie te initiëren op iedere voltooide CompensableActivity die niet is bevestigd of gecompenseerd. Een Compensate-activiteit kan bijvoorbeeld worden gebruikt in de sectie Catches van een TryCatch activiteit of op elk moment nadat de CompensableActivity is voltooid. In dit voorbeeld wordt de Compensate activiteit gebruikt in het gedeelte Catches van een TryCatch activiteit om de actie van de CompensableActivityom te keren.
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
}
}
}
}
};
Dit voorbeeld is de werkstroom in 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>
Wanneer de werkstroom wordt aangeroepen, wordt de volgende uitvoer weergegeven in de console.
ReserveFlight: Ticket is gereserveerd.SimulatedErrorCondition: Een ApplicationException genereren.CancelFlight: Ticket is geannuleerd.Werkstroom is voltooid met de status: Gesloten.
Compensatie bevestigen
Standaard kunnen compensable activiteiten op elk gewenst moment worden gecompenseerd nadat ze zijn voltooid. In sommige scenario's is dit mogelijk niet geschikt. In het vorige voorbeeld was de compensatie voor het reserveren van het ticket het annuleren van de reservering. Nadat de vlucht is voltooid, is deze compensatiestap echter niet meer geldig. Bevestigen dat de compenserende activiteit de door de ConfirmationHandleropgegeven activiteit aanroept. Een mogelijk gebruik hiervoor is om resources die nodig zijn voor de compensatie vrij te maken. Nadat een compenseerbare activiteit is bevestigd, kan deze niet worden gecompenseerd en als er toch geprobeerd wordt, wordt er een InvalidOperationException exceptie gegooid. Wanneer een werkstroom is voltooid, worden alle niet-bevestigde en niet-gecompenseerde compenserende activiteiten die zijn voltooid, bevestigd in omgekeerde volgorde van voltooiing. In dit voorbeeld is de vlucht gereserveerd, aangeschaft en voltooid en wordt de compenserende activiteit bevestigd. Als u een CompensableActivitywilt bevestigen, kunt u de Confirm-activiteit gebruiken en de CompensationToken van de CompensableActivity specificeren om te bevestigen.
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
}
}
};
Dit voorbeeld is de werkstroom in 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>
Wanneer de werkstroom wordt aangeroepen, wordt de volgende uitvoer weergegeven in de console.
ReserveFlight: Ticket is gereserveerd.ManagerApproval: Goedkeuring van manager ontvangen.PurchaseFlight: Ticket is gekocht.TakeFlight: Vlucht is voltooid.ConfirmFlight: Vlucht is genomen, geen compensatie mogelijk.werkstroom is voltooid met de status: Gesloten.
Activiteiten voor compensatie bij nestvorming
Een CompensableActivity kan in het Body gedeelte van een andere CompensableActivityworden geplaatst. Een CompensableActivity kan niet in een handler van een andere CompensableActivityworden geplaatst. Het is de verantwoordelijkheid van een ouder CompensableActivity om ervoor te zorgen dat wanneer het wordt geannuleerd, bevestigd of gecompenseerd, alle activiteiten die zijn voltooid en die nog niet zijn bevestigd of gecompenseerd, moeten worden bevestigd of gecompenseerd voordat de ouder de annulering, bevestiging of compensatie heeft voltooid. Als dit niet expliciet is gemodelleerd, compenseert de bovenliggende CompensableActivity impliciet de onderliggende compenserende activiteiten van het kind als de bovenliggende de annulerings- of compensatiesignaal heeft ontvangen. Als het bovenliggende element het bevestigingssignaal heeft ontvangen, bevestigt het impliciet de compenseerbare handelingen van het kind. Als de logica voor het afhandelen van annulering, bevestiging of compensatie expliciet wordt gemodelleerd in de handler van de moeder CompensableActivity, wordt een kind dat niet expliciet wordt afgehandeld impliciet bevestigd.