Delen via


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 en System.Transactions.Transactionvoor meer informatie over transactiesSystem.Transactions.

CompensableActivity gebruiken

CompensableActivity is de kerncompensatieactiviteit in WF. Activiteiten die werkzaamheden uitvoeren die mogelijk moeten worden gecompenseerd, worden in een BodyCompensableActivity. In dit voorbeeld wordt de reserveringsstap voor het kopen van een vlucht geplaatst in de Body van a CompensableActivity en wordt de annulering van de reservering in de CompensationHandler. Direct na de CompensableActivity werkstroom zijn twee activiteiten die wachten op goedkeuring van de manager en vervolgens de aankoopstap van de vlucht voltooien. Als een foutvoorwaarde ervoor zorgt dat de werkstroom wordt geannuleerd nadat de CompensableActivity werkstroom is voltooid, worden de activiteiten in de CompensationHandler handler 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.De werkstroom is voltooid met de status: Gesloten.

Notitie

De voorbeeldactiviteiten in dit onderwerp, zoals ReserveFlight hun naam en doel weergeven in de console 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 bevestiging is, kan compensatie voor de activiteit niet meer worden aangeroepen. 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: {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();

Wanneer de werkstroom wordt aangeroepen, wordt de uitzondering voor de gesimuleerde foutvoorwaarde verwerkt door de hosttoepassing, OnUnhandledExceptionwordt de werkstroom geannuleerd en wordt de compensatielogica aangeroepen.

ReserveFlight: Ticket is gereserveerd.SimulatedErrorCondition: Een ApplicationException genereren.Niet-verwerkte uitzondering voor werkstroom: System.ApplicationException: Gesimuleerde foutvoorwaarde in de werkstroom.CancelFlight: Ticket is geannuleerd.De werkstroom is voltooid met de status: Geannuleerd.

Annulering en CompensableActivity

Als de activiteiten in een BodyCompensableActivity activiteit niet zijn voltooid en de activiteit wordt geannuleerd, worden de activiteiten in de CancellationHandler activiteiten uitgevoerd.

Notitie

De CancellationHandler activiteit wordt alleen aangeroepen als de activiteiten in de BodyCompensableActivity activiteit niet zijn voltooid en de activiteit wordt geannuleerd. De CompensationHandler uitvoering wordt alleen uitgevoerd als de activiteiten in de BodyCompensableActivity uitvoering zijn voltooid en er vervolgens compensatie wordt aangeroepen voor de activiteit.

De CancellationHandler functie biedt auteurs van werkstromen de mogelijkheid om alle juiste annuleringslogica te bieden. In het volgende voorbeeld wordt er een uitzondering gegenereerd tijdens de uitvoering van de Bodyen vervolgens wordt 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, OnUnhandledExceptionwordt 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 voltooiing is voltooid, betekent dit dat de creditcard in rekening is gebracht en dat de vlucht is geboekt, dus de compensatie moet beide stappen ongedaan maken. (Als u de vlucht annuleert, worden de creditcardkosten automatisch geannuleerd.) Als de CompensableActivity annulering echter wordt geannuleerd, betekent dit dat de Body bewerking niet is voltooid en dus de logica van de CancellationHandler behoeften om te bepalen hoe de annulering het beste kan worden afgehandeld. In dit voorbeeld worden de CancellationHandler kosten van de creditcard geannuleerd, maar sinds ReserveFlight was de laatste activiteit in de Bodyactiviteit, wordt niet geprobeerd de vlucht te annuleren. Omdat ReserveFlight was de laatste activiteit in de Body, als het succesvol was voltooid, zou het Body zijn voltooid en zou er geen annulering mogelijk zijn.

ChargeCreditCard: creditcard in rekening brengen voor vlucht.SimulatedErrorCondition: Een ApplicationException genereren.Niet-verwerkte uitzondering voor werkstroom: System.ApplicationException: Gesimuleerde foutvoorwaarde in de werkstroom.CancelCreditCard: Kosten voor creditcard annuleren.De werkstroom is voltooid met de status: Geannuleerd. Zie Annulering voor 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 de afhandeling van compensatie, kan de Compensate activiteit worden gebruikt. Om het compensatieproces met de Compensate activiteit te initiëren, wordt de CompensationTokenCompensableActivity gewenste compensatie gebruikt. De Compensate activiteit kan worden gebruikt om compensatie te initiëren voor alle voltooide CompensableActivity voltooiden die niet zijn bevestigd of gecompenseerd. Een activiteit kan bijvoorbeeld Compensate worden gebruikt in de Catches sectie van een TryCatch activiteit of op elk moment nadat de CompensableActivity activiteit is voltooid. In dit voorbeeld wordt de Compensate activiteit gebruikt in de Catches sectie van een TryCatch activiteit om de actie van de CompensableActivityactiviteit om 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.De 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. Als u bevestigt dat de compenserende activiteit de activiteit aanroept die is opgegeven door de ConfirmationHandler. Een mogelijk gebruik hiervoor is om alle resources toe te staan die nodig zijn om de compensatie vrij te geven. Nadat een compenserende activiteit is bevestigd, is het niet mogelijk om deze te compenseren en als er wordt geprobeerd een InvalidOperationException uitzondering te genereren. 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, gebruikt u de Confirm activiteit en geeft u de CompensationTokenCompensableActivity te bevestigen waarde op.

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.De werkstroom is voltooid met de status: Gesloten.

Compensatieactiviteiten nesten

Een CompensableActivity kan in de Body sectie 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 onderliggende compenserende activiteiten die succesvol zijn voltooid en nog niet zijn bevestigd of gecompenseerd, moeten worden bevestigd of gecompenseerd voordat de ouder de annulering, bevestiging of compensatie voltooit. Als dit niet expliciet is gemodelleerd, compenseert het CompensableActivity bovenliggende kind impliciet compenserende activiteiten als de ouder het annulerings- of compensatiesignaal heeft ontvangen. Als het bovenliggende element het bevestigingssignaal heeft ontvangen, bevestigt het bovenliggende kind impliciet compensable activiteiten. Als de logica voor het afhandelen van annulering, bevestiging of compensatie expliciet wordt gemodelleerd in de handler van het bovenliggende CompensableActivityitem, wordt elk kind dat niet expliciet wordt verwerkt impliciet bevestigd.

Zie ook