Condividi tramite


Compensazione

Le informazioni contenute in questo argomento sono valide per Windows Workflow Foundation 4.

La compensazione in Windows Workflow Foundation (WF) è il meccanismo mediante il quale il lavoro completato in precedenza può essere annullato o compensato (seguendo la logica definita dall'applicazione) in seguito a un errore. In questa sezione viene illustrato come utilizzare la compensazione nei flussi di lavoro.

Differenze tra compensazione e transazioni

Una transazione consente di combinare più operazioni in un'unica unità di lavoro. Quando viene utilizzata una transazione, l'applicazione può annullare, ovvero eseguire il rollback, di tutte le modifiche eseguite dall'interno della transazione se si verificano errori durante qualsiasi parte del processo della transazione. L'utilizzo di transazioni potrebbe tuttavia non essere adatto per un lavoro a esecuzione prolungata. Ad esempio, un'applicazione di pianificazione di viaggi viene implementata come flusso di lavoro. I passaggi del flusso di lavoro possono essere costituiti dalla prenotazione di un volo, dall'attesa dell'approvazione da parte del responsabile e dal pagamento del volo. Questo processo potrebbe richiedere molti giorni e non è funzionale che i passaggi di prenotazione e pagamento del volo prendano parte alla stessa transazione. In uno scenario come questo, la compensazione potrebbe essere utilizzata per annullare il passaggio di prenotazione del flusso di lavoro se, successivamente, si verifica un errore nell'elaborazione.

Utilizzo di CompensableActivity

L'oggetto CompensableActivity è l'attività di compensazione principale di WF. Qualsiasi attività che esegue un lavoro che necessita di compensazione viene inserita nell'oggetto Body di un oggetto CompensableActivity. In questo esempio il passaggio di prenotazione relativo all'acquisto di un volo viene inserito nell'oggetto Body di un oggetto CompensableActivity, mentre l'annullamento della prenotazione viene inserito nell'oggetto CompensationHandler. Subito dopo l'oggetto CompensableActivity nel flusso di lavoro si trovano due attività che attendono l'approvazione del responsabile e successivamente completano il passaggio di acquisto del volo. Se una condizione di errore provoca l'annullamento del flusso di lavoro dopo il corretto completamento dell'oggetto CompensableActivity, le attività nel gestore CompensationHandler vengono pianificate e il volo viene annullato.

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

L'esempio seguente è il flusso di lavoro 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>

Quando il flusso di lavoro viene richiamato, l'output seguente viene visualizzato nella console.

ReserveFlight: Ticket is reserved. 
ManagerApproval: Manager approval received.
PurchaseFlight: Ticket is purchased.
Workflow completed successfully with status: Closed.
Dd489432.note(it-it,VS.100).gifNota:
Nelle attività di esempio in questo argomento, quale ReserveFlight, vengono visualizzati nome e scopo nella console per consentire di illustrare l'ordine in cui vengono eseguite le attività quando si verifica la compensazione.

Compensazione del flusso di lavoro predefinita

Per impostazione predefinita, se il flusso di lavoro viene annullato, viene eseguita la logica di compensazione per qualsiasi attività compensabile che è stata completata correttamente e non è stata già confermata o compensata.

Dd489432.note(it-it,VS.100).gifNota:
Quando un oggetto CompensableActivity viene confermato, la compensazione per l'attività non può essere più richiamata. Il processo di conferma verrà illustrato più avanti in questa sezione.

In questo esempio viene generata un'eccezione dopo la prenotazione del volo ma prima del passaggio di approvazione da parte del responsabile.

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

In questo esempio viene illustrato il flusso di lavoro 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();

Quando viene richiamato il flusso di lavoro, l'eccezione della condizione di errore simulata viene gestita dall'applicazione host in OnUnhandledException, il flusso di lavoro viene annullato e la logica di compensazione viene richiamata.

ReserveFlight: Ticket is reserved. 
SimulatedErrorCondition: Throwing an ApplicationException.
Workflow Unhandled Exception:
System.ApplicationException: Simulated error condition in the workflow.
CancelFlight: Ticket is canceled.
Workflow completed successfully with status: Canceled.

Annullamento e CompensableActivity

Se le attività in Body di un oggetto CompensableActivity non sono state completate e l'attività è annullata, vengono eseguite le attività in CancellationHandler.

Dd489432.note(it-it,VS.100).gifNota:
CancellationHandler viene richiamato solo se le attività nell'oggetto Body dell'oggetto CompensableActivity non sono state completate e l'attività è annullata. CompensationHandler viene eseguito solo se le attività nell'oggetto Body dell'oggetto CompensableActivity sono state completate correttamente e viene successivamente richiamata la compensazione sull'attività.

CancellationHandler fornisce offre agli autori del flusso di lavoro la possibilità per fornire la logica di annullamento appropriata. Nell'esempio seguente viene generata un'eccezione durante l'esecuzione di Body, quindi viene richiamato CancellationHandler.

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

Questo esempio è il flusso di lavoro 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>

Quando viene richiamato il flusso di lavoro, l'eccezione della condizione di errore simulata viene gestita dall'applicazione host in OnUnhandledException, il flusso di lavoro viene annullato e viene richiamata la logica di cancellazione di CompensableActivity. In questo esempio sia la logica di compensazione che la logica di annullamento hanno lo stesso obiettivo, ovvero annullare il volo riservato.

ReserveFlight: Ticket is reserved. 
SimulatedErrorCondition: Throwing an ApplicationException.
Workflow Unhandled Exception:
System.ApplicationException: Simulated error condition in the workflow.
CancelFlight: Ticket is canceled.
Workflow completed successfully with status: Canceled.

Per ulteriori informazioni su annullamento, vedere Modellazione del comportamento di annullamento nei flussi di lavoro.

Compensazione esplicita tramite l'attività Compensate

Nella sezione precedente è stata illustrata la compensazione implicita. Si tratta di un tipo di compensazione che può risultare appropriata per scenari semplici, ma se occorre un controllo più esplicito sulla pianificazione della gestione della compensazione è possibile utilizzare l'attività Compensate. Per iniziare il processo di compensazione con l'attività Compensate, viene utilizzato l'oggetto CompensationToken dell'oggetto CompensableActivity per il quale si desidera la compensazione. L'attività Compensate può essere utilizzata per iniziare la compensazione su qualsiasi oggetto CompensableActivity completato che non è stato confermato o compensato. Ad esempio, un'attività Compensate potrebbe essere utilizzata nella sezione Catches di un'attività TryCatch o in qualsiasi momento dopo il completamento dell'oggetto CompensableActivity. In questo esempio l'attività Compensate viene utilizzata nell'oggetto Catches di un'attività TryCatch per invertire l'azione dell'oggetto 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
                }
            }
        }
    }
};

In questo esempio viene illustrato il flusso di lavoro 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>

Quando il flusso di lavoro viene richiamato, l'output seguente viene visualizzato nella console.

ReserveFlight: Ticket is reserved. 
SimulatedErrorCondition: Throwing an ApplicationException.
CancelFlight: Ticket is canceled.
Workflow completed successfully with status: Closed.

Conferma della compensazione

Per impostazione predefinita, le attività compensabili possono essere compensate in qualsiasi momento una volta completate. In alcuni casi però questo potrebbe non essere appropriato. Nell'esempio precedente la compensazione relativa alla prenotazione del biglietto consisteva nell'annullamento della prenotazione. Tuttavia, una volta completato il volo, questo passaggio di compensazione non è più valido. La conferma dell'attività compensabile richiama l'attività specificata da ConfirmationHandler. Un possibile utilizzo consiste nel consentire a qualsiasi risorsa necessaria di eseguire la compensazione da rilasciare. Una volta confermata, un'attività compensabile non può essere compensata e se si tenta tale operazione, verrà generata un'eccezione InvalidOperationException. Quando un flusso di lavoro viene completato correttamente, tutte le attività compensabili non confermate e non compensate completate correttamente vengono confermate nell'ordine inverso rispetto al completamento. In questo esempio il volo è prenotato, acquistato e completato, quindi l'attività compensabile è confermata. Per confermare un oggetto CompensableActivity, utilizzare l'attività Confirm e specificare l'oggetto CompensationToken dell'oggetto CompensableActivity da confermare.

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

In questo esempio viene illustrato il flusso di lavoro 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>

Quando il flusso di lavoro viene richiamato, l'output seguente viene visualizzato nella console.

ReserveFlight: Ticket is reserved. 
ManagerApproval: Manager approval received.
PurchaseFlight: Ticket is purchased.
TakeFlight: Flight is completed.
ConfirmFlight: Flight has been taken, no compensation possible.
Workflow completed successfully with status: Closed.

Annidamento delle attività di compensazione

Un oggetto CompensableActivity può essere posizionato nella sezione Body di un altro oggetto CompensableActivity. In tal caso, è compito dell'oggetto CompensableActivity padre gestire la compensazione e la conferma dell'oggetto CompensableActivity figlio in un modo appropriato allo scopo del flusso di lavoro. Se non viene richiamata alcuna conferma o compensazione esplicita, l'oggetto CompensableActivity figlio viene confermato quando l'attività padre viene confermata, ma la compensazione non viene richiamata per l'attività figlio quando richiamata nell'attività padre.

Vedere anche

Attività

Esempio di CompensableActivity

Riferimento

CompensableActivity
Compensate
Confirm
CompensationToken

Altre risorse

Compensation Programming Model
Compensation