Partager via


Compensation

Cette rubrique s'applique à Windows Workflow Foundation 4.

La compensation dans Windows Workflow Foundation (WF) est le mécanisme qui permet d'annuler ou de compenser un travail terminé (en fonction de la logique définie par l'application) lorsqu'un échec ultérieur se produit. Cette section décrit comment utiliser une compensation dans les flux de travail.

Compensation contre Transactions

Une transaction vous permet de combiner plusieurs opérations en une seule unité de travail. L'utilisation d'une transaction permet à votre application d'annuler (restaurer) toute modification exécutée depuis une transaction en cas d'erreur au cours du processus de transaction. Toutefois, l'utilisation de transactions peut ne pas convenir dans le cas d'un travail de longue durée. Par exemple, une application de planification de voyage est implémentée en tant que flux de travail. Les étapes du flux de travail peuvent porter sur la réservation d'un vol, l'attente de l'approbation du gestionnaire et le paiement du vol. Ce processus pourrait prendre de nombreux jours et ne s'avère pas pratique pour que les étapes de réservation et de paiement du vol puissent participer à la même transaction. Dans un tel scénario, la compensation pourrait être utilisée pour annuler l'étape de réservation du flux de travail en cas d'erreur ultérieure lors du traitement.

Utilisation de CompensableActivity

CompensableActivity est l'activité de compensation principale dans WF. Toutes les activités qui effectuent un travail pouvant nécessiter d'être compensé sont placées dans le Body d'un CompensableActivity. Dans cet exemple, l'étape de réservation de l'achat d'un vol est placée dans le Body d'un CompensableActivity et l'annulation de la réservation est placée dans le CompensationHandler. Juste après le CompensableActivity dans le workflow, deux activités doivent être exécutées, d'une part l'approbation du gestionnaire, d'autre part l'achat du vol. Si une condition d'erreur entraîne l'annulation du workflow une fois CompensableActivity correctement terminé, les activités du gestionnaire CompensationHandler sont planifiées et le vol est annulé.

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

L'exemple suivant est le workflow en 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>

Lorsque le workflow est appelé, la sortie suivante s'affiche sur la console.

ReserveFlight : le billet est réservé. 
ManagerApproval : l'approbation du gestionnaire a été reçue.
PurchaseFlight : le billet est acheté.
Le workflow s'est terminé avec succès avec l'état Fermé.
Dd489432.note(fr-fr,VS.100).gifRemarque :
Les exemples d'activités de cette rubrique, telles que ReserveFlight, affichent leur nom et leur but dans la console pour faciliter l'illustration de l'ordre dans lequel les activités sont exécutées lorsque la compensation se produit.

Compensation de workflow par défaut

Par défaut, si le workflow est annulé, la logique de compensation est exécutée pour toute activité compensable ayant abouti et n'ayant pas encore été confirmée ou compensée.

Dd489432.note(fr-fr,VS.100).gifRemarque :
Lorsqu'un CompensableActivity est confirmé, la compensation pour l'activité ne peut plus être appelée. Le processus de confirmation est décrit plus loin dans cette section.

Dans cet exemple, une exception est levée après que le vol a été réservé mais avant l'étape d'approbation par le gestionnaire.

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

Cet exemple est le workflow en 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();

Lorsque le workflow est appelé, l'exception de condition d'erreur simulée est gérée par l'application hôte dans OnUnhandledException, le workflow est annulé, et la logique de compensation est appelée.

ReserveFlight : le billet est réservé. 
SimulatedErrorCondition : levée d'un ApplicationException.
Exception non prise en charge du workflow :
System.ApplicationException : condition d'erreur simulée dans le workflow.
CancelFlight : le billet est annulé.
Le workflow s'est terminé avec succès avec l'état Annulé.

Annulation et CompensableActivity

Si les activités dans le Body d'un CompensableActivity ne se sont pas terminées et que l'activité est annulée, les activités dans le CancellationHandler sont exécutées.

Dd489432.note(fr-fr,VS.100).gifRemarque :
CancellationHandler est appelé uniquement si les activités dans le Body de CompensableActivity ne se sont pas terminées et que l'activité est annulée. CompensationHandler est exécuté uniquement si les activités dans le Body de CompensableActivity se sont terminées avec succès et que la compensation est, par la suite, appelée sur l'activité.

CancellationHandler donne aux auteurs de workflow la possibilité de fournir n'importe quelle logique d'annulation appropriée. Dans l'exemple suivant, une exception est levée pendant l'exécution de Body, puis CancellationHandler est appelé.

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

Cet exemple est le workflow en 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>

Lorsque le workflow est appelé, l'exception de condition d'erreur simulée est gérée par l'application hôte dans OnUnhandledException, le workflow est annulé, et la logique d'annulation de CompensableActivity est appelée. Dans cet exemple, la logique de compensation et la logique d'annulation ont toutes les deux le même objectif : annuler le vol réservé.

ReserveFlight : le billet est réservé. 
SimulatedErrorCondition : levée d'un ApplicationException.
Exception non prise en charge du workflow :
System.ApplicationException : condition d'erreur simulée dans le workflow.
CancelFlight : le billet est annulé.
Le workflow s'est terminé avec succès avec l'état Annulé.

Pour plus d'informations sur le sujet suivant l'annulation, consultez Modélisation du comportement d'annulation dans les workflows.

Compensation explicite à l'aide de l'activité Compensate

Dans la section précédente, la compensation implicite a été couverte. La compensation implicite peut convenir à des scénarios simples, mais si un contrôle explicite supplémentaire est requis sur la planification de compensation, la gestion de l'activité Compensate peut être utilisée. Pour initialiser le processus de compensation avec l'activité Compensate, le CompensationToken du CompensableActivity pour lequel la compensation est désirée est utilisé. L'activité Compensate peut être utilisée pour initialiser la compensation sur tout CompensableActivity ayant abouti et qui n'a pas été confirmé ou compensé. Par exemple, une activité Compensate pourrait être utilisée dans la section Catches d'une activité TryCatch, ou à n'importe quel moment après que le CompensableActivity a abouti. Dans cet exemple, l'activité Compensate est utilisée dans le Catches d'une activité TryCatch pour inverser l'action de 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
                }
            }
        }
    }
};

Cet exemple est le workflow en 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>

Lorsque le workflow est appelé, la sortie suivante s'affiche sur la console.

ReserveFlight : le billet est réservé. 
SimulatedErrorCondition : levée d'un ApplicationException.
CancelFlight : le billet est annulé.
Le workflow s'est terminé avec succès avec l'état Fermé.

Confirmer la compensation

Par défaut, les activités compensables peuvent être compensées à n'importe quel moment, à condition qu'elles soient achevées. Toutefois, dans certains cas cela peut ne pas être suffisant. Dans l'exemple précédent la compensation destinée à réserver le ticket devait permettre d'annuler la réservation. Toutefois, une fois le vol effectué cette étape de compensation n'est plus valide. La confirmation de l'activité compensable appelle l'activité spécifiée dans la section ConfirmationHandler. Une utilisation possible de cela est de permettre la libération de toutes les ressources qui sont nécessaires pour effectuer la compensation. Lorsqu'une activité compensable est confirmée, il n'est pas possible de la dédommager, et si vous tentez cette opération, une exception InvalidOperationException est levée. Lorsqu'un flux de travail aboutit, toutes les activités non confirmées et non dédommagées ayant abouti sont confirmées dans l'ordre inverse d'achèvement. Dans cet exemple le vol est réservé, acheté et finalisé, puis, l'activité compensable est confirmée. Pour confirmer un CompensableActivity, utilisez l'activité Confirm et spécifiez le CompensationToken du CompensableActivity à confirmer.

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

Cet exemple est le workflow dans 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>

Lorsque le workflow est appelé, la sortie suivante s'affiche sur la console.

ReserveFlight : le billet est réservé. 
ManagerApproval : l'approbation du gestionnaire a été reçue.
PurchaseFlight : le billet est acheté.
TakeFlight : le vol est effectué.
ConfirmFlight : le vol a été pris, aucune compensation possible.
Le workflow s'est terminé avec succès avec l'état Fermé.

Imbrication d'activités de compensation

Un CompensableActivity peut être placé dans la section Body d'un autre CompensableActivity. Lorsque cela se produit, c'est la responsabilité du CompensableActivity parent de gérer la compensation et la confirmation du CompensableActivity enfant de façon à répondre à l'objectif du workflow. Si aucune confirmation explicite ou compensation n'est appelée, le CompensableActivity enfant est confirmé lorsque l'activité parent est confirmée, mais la compensation n'est pas appelée pour l'activité enfant lorsque la compensation est appelée sur l'activité parent.

Voir aussi

Tâches

Exemple d'activité compensable

Référence

CompensableActivity
Compensate
Confirm
CompensationToken

Autres ressources

Compensation Programming Model
Compensation