Freigeben über


Kompensierung

Dieses Thema gilt für Windows Workflow Foundation 4.

Die Kompensation in Windows Workflow Foundation (WF) ist der Mechanismus, mit dem zuvor abgeschlossene Arbeitsaufgaben rückgängig gemacht bzw. kompensiert werden können (gemäß der von der Anwendung definierten Logik), wenn nachfolgend ein Fehler auftritt. In diesem Abschnitt wird beschrieben, wie die Kompensation in Workflows verwendet wird.

Kompensation und Transaktionen

Eine Transaktion ermöglicht es Ihnen, mehrere Vorgänge in nur einer Arbeitseinheit zu kombinieren. Wenn Sie Transaktionen verwenden, kann Ihre Anwendung alle Änderungen zurücknehmen (Rollback), die innerhalb der Transaktion ausgeführt wurden, falls während des Transaktionsprozesses ein Fehler auftritt. Die Verwendung von Transaktionen eignet sich jedoch möglicherweise nicht für Arbeitsaufgaben mit langer Laufzeit. Angenommen, eine Anwendung zum Planen von Reisen wird als Workflow implementiert. Die Schritte des Workflows bestehen z. B. aus der Buchung eines Flugs, dem Warten auf die Genehmigung des Managers und der anschließenden Bezahlung des Flugs. Da sich dieser Prozess über mehrere Tage hinziehen kann, ist es nicht sinnvoll, die Buchungs- und Zahlungsschritte in derselben Transaktion zu implementieren. In einem Szenario wie diesem kann der Buchungsschritt des Workflows mittels Kompensation rückgängig gemacht werden, wenn an späterer Stelle ein Verarbeitungsfehler auftritt.

Verwenden von CompensableActivity

Das CompensableActivity-Objekt ist die wichtigste Kompensationsaktivität in WF. Alle Aktivitäten zum Ausführen einer Arbeit, die möglicherweise kompensiert werden soll, werden in das Body-Element eines CompensableActivity-Objekts eingefügt. In diesem Beispiel wird der Reservierungsschritt der Buchung eines Flugs in das Body-Element eines CompensableActivity-Objekts eingefügt, und der Abbruch der Reservierung wird in das CompensationHandler-Element eingefügt. Im Workflow folgen unmittelbar auf das CompensableActivity-Objekt zwei Aktivitäten, für die eine Genehmigung durch einen Manager erfolgen muss und die dann die Buchung des Flugs abschließen. Wenn der Workflow aufgrund eines Fehlers abgebrochen wird, nachdem das CompensableActivity-Objekt erfolgreich abgeschlossen wurde, werden die Aktivitäten im CompensationHandler-Handler geplant und der Flug wird gestrichen.

Activity wf = new Sequence()
{
    Activities =
    {
        new CompensableActivity
        {
            Body = new ReserveFlight(),
            CompensationHandler = new CancelFlight()
        },
        new ManagerApproval(),
        new PurchaseFlight()
    }
};

Das folgende Beispiel ist der Workflow in XAML.

<Sequence
   xmlns="https://schemas.microsoft.com/netfx/2009/xaml/activities"
   xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
   xmlns:x="https://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>

Wenn der Workflow aufgerufen wird, wird die folgende Ausgabe in der Konsole angezeigt.

ReserveFlight: Ticket wird reserviert. 
ManagerApproval: Genehmigung durch einen Manager empfangen.
PurchaseFlight: Ticket wird gekauft.
Workflow wurde erfolgreich mit dem Status Geschlossen abgeschlossen.
Dd489432.note(de-de,VS.100).gifHinweis:
Die Beispielaktivitäten in diesem Thema z. B. ReserveFlight geben ihren Namen und Zweck auf der Konsole an, um die Reihenfolge zu veranschaulichen, in der die Aktivitäten ausgeführt werden, wenn eine Kompensation auftritt.

Standard-Workflowkompensation

Standardmäßig wird nach dem Abbruch eines Workflows die Kompensationslogik für jede kompensierbare Aktivität ausgeführt, die erfolgreich abgeschlossen wurde und noch nicht bestätigt oder kompensiert wurde.

Dd489432.note(de-de,VS.100).gifHinweis:
Wenn ein CompensableActivity-Objekt bestätigt wird, kann für die Aktivität keine Kompensation mehr aufgerufen werden. Der Bestätigungsvorgang wird weiter unten in diesem Abschnitt beschrieben.

In diesem Beispiel wird eine Ausnahme ausgelöst, nachdem der Flug reserviert wurde und bevor der Genehmigungsschritt durch den Manager erfolgen konnte.

Activity wf = new Sequence()
{
    Activities =
    {
        new CompensableActivity
        {
            Body = new ReserveFlight(),
            CompensationHandler = new CancelFlight()
        },
        new SimulatedErrorCondition(),
        new ManagerApproval(),
        new PurchaseFlight()
    }
};

Dieses Beispiel ist der Workflow in XAML.

<Sequence
   xmlns="https://schemas.microsoft.com/netfx/2009/xaml/activities"
   xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
   xmlns:x="https://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();

Wenn der Workflow aufgerufen wird, wird die simulierte Fehlerbedingungsausnahme von der Hostanwendung in OnUnhandledException behandelt, der Workflow wird abgebrochen, und die Kompensationslogik wird aufgerufen.

ReserveFlight: Ticket wird reserviert. 
SimulatedErrorCondition: Auslösen von ApplicationException.
Nicht behandelte Workflowausnahme:
System.ApplicationException: Simulierte Fehlerbedingung im Workflow.
CancelFlight: Ticket wird storniert.
Workflow wurde erfolgreich mit dem Status Abgebrochen abgeschlossen.

Abbruch und CompensableActivity

Wenn die Aktivitäten in Body eines CompensableActivity-Objekts nicht abgeschlossen wurden und die Aktivität abgebrochen wird, werden die Aktivitäten in CancellationHandler ausgeführt.

Dd489432.note(de-de,VS.100).gifHinweis:
CancellationHandler wird nur aufgerufen, wenn die Aktivitäten in Body des CompensableActivity-Objekts nicht abgeschlossen wurden und die Aktivität abgebrochen wird. CompensationHandler wird nur ausgeführt, wenn die Aktivitäten in Body des CompensableActivity-Objekts erfolgreich abgeschlossen wurden, und die Kompensation wird anschließend für die Aktivität aufgerufen.

CancellationHandler gibt Workflowautoren die Gelegenheit, eine geeignete Abbruchlogik bereitzustellen. Im folgenden Beispiel wird eine Ausnahme während der Ausführung von Body ausgelöst, und dann wird CancellationHandler aufgerufen.

Activity wf = new Sequence()
{
    Activities =
    {
        new CompensableActivity
        {
            Body = new Sequence
            {
                Activities = 
                {
                    new ReserveFlight(),
                    new SimulatedErrorCondition()
                }
            },
            CompensationHandler = new CancelFlight(),
            CancellationHandler = new CancelFlight()
        },
        new ManagerApproval(),
        new PurchaseFlight()
    }
};

Dieses Beispiel ist der Workflow in XAML.

<Sequence
   xmlns="https://schemas.microsoft.com/netfx/2009/xaml/activities"
   xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
   xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml">
  <CompensableActivity>
    <CompensableActivity.Result>
      <OutArgument
         x:TypeArguments="CompensationToken" />
    </CompensableActivity.Result>
    <Sequence>
      <c:ReserveFlight />
      <c:SimulatedErrorCondition />
    </Sequence>
    <CompensableActivity.CancellationHandler>
      <c:CancelFlight />
    </CompensableActivity.CancellationHandler>
    <CompensableActivity.CompensationHandler>
      <c:CancelFlight />
    </CompensableActivity.CompensationHandler>
  </CompensableActivity>
  <c:ManagerApproval />
  <c:PurchaseFlight />
</Sequence>

Wenn der Workflow aufgerufen wird, wird die simulierte Fehlerbedingungsausnahme von der Hostanwendung in OnUnhandledException behandelt, der Workflow wird abgebrochen, und die Abbruchlogik von CompensableActivity wird aufgerufen. In diesem Beispiel haben sowohl die Kompensationslogik als auch die Abbruchlogik das gleiche Ziel, den reservierten Flug zu streichen.

ReserveFlight: Ticket wird reserviert. 
SimulatedErrorCondition: Auslösen von ApplicationException.
Nicht behandelte Workflowausnahme:
System.ApplicationException: Simulierte Fehlerbedingung im Workflow.
CancelFlight: Ticket wird storniert.
Workflow wurde erfolgreich mit dem Status Abgebrochen abgeschlossen.

Weitere Informationen über zu Abbruch finden Sie unter Modellieren des Abbruchverhaltens in Workflows.

Explizite Kompensation mit Verwendung der Compensate-Aktivität

Im vorherigen Abschnitt wurde die implizite Kompensation behandelt. Eine implizite Kompensation eignet sich für einfache Szenarien. Wenn jedoch mehr explizite Steuerungsmöglichkeiten für das Planen der Kompensationsbehandlung erforderlich sind, kann die Compensate-Aktivität verwendet werden. Um den Kompensationsprozess mit der Compensate-Aktivität zu initiieren, wird das CompensationToken-Objekt des CompensableActivity-Objekts verwendet, für das eine Kompensation möglich sein soll. Mit der Compensate-Aktivität kann eine Kompensation für ein beliebiges abgeschlossenes CompensableActivity-Objekt initiiert werden, solange dieses nicht bestätigt oder kompensiert wurde. Beispielsweise kann eine Compensate-Aktivität im Abschnitt Catches einer TryCatch-Aktivität oder jederzeit nach Abschluss des CompensableActivity-Objekts verwendet werden. In diesem Beispiel wird die Compensate-Aktivität in Catches einer TryCatch-Aktivität dazu verwendet, die Aktion des CompensableActivity-Objekts rückgängig zu machen.

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
                }
            }
        }
    }
};

Dieses Beispiel ist der Workflow in XAML.

<TryCatch
   xmlns="https://schemas.microsoft.com/netfx/2009/xaml/activities"
   xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
   xmlns:s="clr-namespace:System;assembly=mscorlib"
   xmlns:x="https://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>

Wenn der Workflow aufgerufen wird, wird die folgende Ausgabe in der Konsole angezeigt.

ReserveFlight: Ticket wird reserviert. 
SimulatedErrorCondition: Auslösen von ApplicationException.
CancelFlight: Ticket wird storniert.
Workflow wurde erfolgreich mit dem Status Geschlossen abgeschlossen.

Bestätigen einer Kompensation

Standardmäßig können kompensierbare Aktivitäten jederzeit kompensiert werden, nachdem sie abgeschlossen wurden. Für einige Szenarien eignet sich diese Vorgehensweise möglicherweise nicht. Im vorherigen Beispiel bestand die Kompensation bei der Reservierung des Tickets darin, den Reservierungsvorgang abzubrechen. Wenn der Flug jedoch stattgefunden hat, ist dieser Kompensationsschritt nicht mehr gültig. Bei der Bestätigung der kompensierbaren Aktivität wird die Aktivität aufgerufen, die von ConfirmationHandler angegeben wird. Eine Verwendungsmöglichkeit dafür besteht darin, alle Ressourcen freizugeben, die für die Durchführung der Kompensation erforderlich sind. Sobald eine kompensierbare Aktivität bestätigt wird, kann sie nicht mehr kompensiert werden. Falls dies dennoch versucht wird, wird eine InvalidOperationException-Ausnahme ausgelöst. Wenn ein Workflow erfolgreich abgeschlossen wird, werden alle nicht bestätigten und nicht kompensierbaren Aktivitäten, die erfolgreich abgeschlossen wurden, in umgekehrter Reihenfolge Ihres Abschlusses bestätigt. In diesem Beispiel wird der Flug reserviert, gebucht und abgeschlossen, und dann wird die kompensierbare Aktivität bestätigt. Um ein CompensableActivity-Objekt zu bestätigen, verwenden Sie die Confirm-Aktivität, und geben Sie das CompensationToken-Objekt des CompensableActivity-Objekts an, das bestätigt werden soll.

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
        }
    }
};

Dieses Beispiel ist der Workflow in XAML.

<Sequence
   xmlns="https://schemas.microsoft.com/netfx/2009/xaml/activities"
   xmlns:c="clr-namespace:CompensationExample;assembly=CompensationExample"
   xmlns:x="https://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>

Wenn der Workflow aufgerufen wird, wird die folgende Ausgabe in der Konsole angezeigt.

ReserveFlight: Ticket wird reserviert. 
ManagerApproval: Genehmigung durch einen Manager empfangen.
PurchaseFlight: Ticket wird gekauft.
TakeFlight: Flug wird abgeschlossen.
ConfirmFlight: Flug wurde angetreten, keine Kompensation möglich.
Workflow wurde erfolgreich mit dem Status Geschlossen abgeschlossen.

Schachteln von Kompensationsaktivitäten

Ein CompensableActivity-Objekt kann in den Abschnitt Body eines anderen CompensableActivity-Objekts eingefügt werden. Wenn dies geschieht, ist das übergeordnete CompensableActivity-Objekt dafür zuständig, die Kompensation und die Bestätigung für das untergeordnete CompensableActivity-Objekt auf eine Weise zu behandeln, die für den Zweck des Workflows geeignet ist. Wenn keine explizite Bestätigung oder Kompensation aufgerufen wird, wird das untergeordnete CompensableActivity-Objekt bestätigt, wenn die übergeordnete Aktivität bestätigt wird. Die Kompensation wird jedoch nicht für die untergeordnete Aktivität aufgerufen, wenn die Kompensation für die übergeordnete Aktivität aufgerufen wird.

Siehe auch

Aufgaben

Kompensierbare Aktivität – Beispiel

Verweis

CompensableActivity
Compensate
Confirm
CompensationToken

Weitere Ressourcen

Compensation Programming Model
Compensation