Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
La compensazione in Windows Workflow Foundation (WF) è il meccanismo in base al quale il lavoro completato in precedenza può essere annullato o compensato (seguendo la logica definita dall'applicazione) quando si verifica un errore successivo. Questa sezione descrive come usare la compensazione nei flussi di lavoro.
Compensazione e transazioni
Una transazione consente di combinare più operazioni in una singola unità di lavoro. L'uso di una transazione consente all'applicazione di interrompere (eseguire il rollback) di tutte le modifiche eseguite dall'interno della transazione se si verificano errori durante qualsiasi parte del processo di transazione. Tuttavia, l'uso delle transazioni potrebbe non essere appropriato se il lavoro è a lungo termine. Ad esempio, un'applicazione di pianificazione 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 del manager e dal pagamento del volo. Questo processo potrebbe richiedere molti giorni e non è pratico per i passaggi di prenotazione e pagamento del volo per partecipare alla stessa transazione. In uno scenario come questo, la compensazione può essere usata per annullare il passaggio di prenotazione del flusso di lavoro se si verifica un errore in un secondo momento nell'elaborazione.
Nota
Questo argomento illustra la compensazione nei flussi di lavoro. Per altre informazioni sulle transazioni nei flussi di lavoro, vedere Transazioni e TransactionScope. Per altre informazioni sulle transazioni, vedere System.Transactions e System.Transactions.Transaction.
L'uso di "CompensableActivity"
CompensableActivity è l'attività di compensazione principale in WF. Tutte le attività che eseguono operazioni che possono essere compensate vengono inserite nella Body di un CompensableActivity. In questo esempio, la fase di prenotazione di un volo viene inserita nella Body di un CompensableActivity e l'annullamento della prenotazione viene inserito nel CompensationHandler. Subito dopo il CompensableActivity nel flusso di lavoro, ci sono due attività che attendono l'approvazione del responsabile e quindi completano la procedura d'acquisto del volo. Se una condizione di errore determina l'annullamento del flusso di lavoro dopo il completamento con successo del CompensableActivity, le attività nel gestore CompensationHandler vengono programmate 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="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>
Quando viene richiamato il flusso di lavoro, viene visualizzato l'output seguente nella console.
ReserveFlight: il biglietto è riservato.ManagerApproval: approvazione del manager ricevuta.PurchaseFlight: il biglietto è stato acquistato.Flusso di lavoro completato correttamente con stato: Chiuso.
Nota
Le attività di esempio in questo argomento, come ReserveFlight
, visualizzano il loro nome e scopo sulla console per illustrare l'ordine con cui vengono eseguite quando si verifica una compensazione.
Compensazione del flusso di lavoro predefinita
Per impostazione predefinita, se il flusso di lavoro viene annullato, la logica di compensazione viene eseguita per qualsiasi attività compensabile che abbia avuto esito positivo e non sia già stata confermata o compensata.
Nota
Quando un CompensableActivity viene confermato, la compensazione per l'attività non può più essere accordata. Il processo di conferma viene descritto più avanti in questa sezione.
In questo esempio viene generata un'eccezione dopo la prenotazione del volo, ma prima del passaggio di approvazione del manager.
Activity wf = new Sequence()
{
Activities =
{
new CompensableActivity
{
Body = new ReserveFlight(),
CompensationHandler = new CancelFlight()
},
new SimulatedErrorCondition(),
new ManagerApproval(),
new PurchaseFlight()
}
};
Questo esempio è il flusso di lavoro 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:
{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:
{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 viene richiamata la logica di compensazione.
ReserveFlight: il biglietto è riservato.SimulatedErrorCondition: scatenare un'ApplicationException.Eccezione non gestita del flusso di lavoro:System.ApplicationException: condizione di errore simulata nel flusso di lavoro.CancelFlight: il biglietto viene annullato.Flusso di lavoro completato correttamente con lo stato: Annullato.
Annullamento e Attività Risarcibile
Se le attività nella Body di un CompensableActivity non sono state completate e l'attività viene annullata, le attività nel CancellationHandler vengono eseguite.
Nota
Il CancellationHandler viene richiamato solo se le attività nel Body del CompensableActivity non sono state completate e l'attività viene annullata. Il CompensationHandler viene eseguito solo se le attività nel Body del CompensableActivity sono state completate con successo e la compensazione viene successivamente richiamata su quell'attività.
Il CancellationHandler offre agli autori del flusso di lavoro la possibilità di fornire qualsiasi logica di annullamento appropriata. Nell'esempio seguente viene generata un'eccezione durante l'esecuzione del Bodye quindi viene richiamata la CancellationHandler.
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()
}
};
Questo esempio è il flusso di lavoro 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>
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 annullamento del CompensableActivity. In questo esempio la logica di compensazione e la logica di annullamento hanno obiettivi diversi. Se il Body è completato con successo, significa che la carta di credito è stata addebitata e il volo prenotato, quindi la compensazione dovrebbe restituire entrambi i passaggi. In questo esempio, l'annullamento del volo annulla automaticamente gli addebiti della carta di credito. Tuttavia, se il CompensableActivity viene annullato, significa che il Body non è stato completato e quindi la logica del CancellationHandler deve essere in grado di determinare come gestire al meglio l'annullamento. In questo esempio, il CancellationHandler annulla l'addebito della carta di credito, ma poiché ReserveFlight
è stata l'ultima attività nel Body, non tenta di annullare il volo. Poiché ReserveFlight
era l'ultima attività nel Body, se fosse stata completata correttamente, il Body sarebbe stato completato e non sarebbe possibile annullare.
ChargeCreditCard: addebito della carta di credito per il volo.SimulatedErrorCondition: genera un'eccezione ApplicationException.Eccezione non gestita del Flusso di lavoro:System.ApplicationException: condizione di errore simulata nel Flusso di lavoro.CancelCreditCard: annulla gli addebiti della carta di credito.Flusso di lavoro completato correttamente con lo stato di 'Annullata'. Per altre informazioni sull'annullamento, vedere Annullamento.
Compensazione esplicita mediante l'attività di compensazione
Nella sezione precedente è stata trattata la compensazione implicita. La compensazione implicita può essere appropriata per scenari semplici, ma se è necessario un controllo più esplicito sulla pianificazione della gestione della compensazione, è possibile usare l'attività Compensate. Per avviare il processo di compensazione con l'attività Compensate, il CompensationToken del CompensableActivity per cui è richiesta la compensazione viene utilizzato. L'attività Compensate può essere utilizzata per avviare una compensazione su qualsiasi CompensableActivity completato che non è stato confermato o compensato. Ad esempio, un'attività Compensate può essere usata nella sezione Catches di un'attività TryCatch o in qualsiasi momento dopo il completamento del CompensableActivity. In questo esempio, l'attività Compensate viene usata nella sezione Catches di un'attività TryCatch per invertire l'azione del 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
}
}
}
}
};
Questo esempio è il flusso di lavoro 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>
Quando viene richiamato il flusso di lavoro, viene visualizzato l'output seguente nella console.
ReserveFlight: Biglietto prenotato.SimulatedErrorCondition: Lancia un'eccezione di tipo ApplicationException.CancelFlight: Il biglietto viene annullato.Flusso di lavoro completato correttamente con lo stato: Chiuso.
Conferma della compensazione
Per impostazione predefinita, le attività compensabili possono essere compensate in qualsiasi momento dopo il completamento. In alcuni scenari questo potrebbe non essere appropriato. Nell'esempio precedente la compensazione per prenotare il biglietto era annullare la prenotazione. Tuttavia, dopo che il volo è stato completato questo passaggio di compensazione non è più valido. La conferma dell'attività compensabile richiama l'attività specificata dal ConfirmationHandler. Un possibile uso per questo è consentire il rilascio di tutte le risorse necessarie per eseguire la compensazione. Dopo che un'attività compensabile viene confermata, non è possibile che venga compensata e se si tenta di eseguire un'eccezione InvalidOperationException viene generata. Quando un flusso di lavoro viene completato correttamente, tutte le attività compensabili non confermate e non compensate che sono state completate correttamente vengono confermate in ordine inverso di completamento. In questo esempio il volo è riservato, acquistato e completato e quindi viene confermata l'attività compensabile. Per confermare un CompensableActivity, usa l'attività Confirm e specifica il CompensationToken del CompensableActivity per 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
}
}
};
Questo esempio è il flusso di lavoro 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>
Quando viene richiamato il flusso di lavoro, viene visualizzato l'output seguente nella console.
ReserveFlight: il biglietto è riservato.ManagerApproval: approvazione del manager ricevuta.PurchaseFlight: il ticket viene acquistato.TakeFlight: il volo è completato.ConfirmFlight: il volo è stato preso, nessun risarcimento possibile.Flusso di lavoro completato correttamente con stato: Chiuso.
Organizzazione delle attività di compensazione
Un CompensableActivity può essere inserito nella sezione Body di un altro CompensableActivity. Un CompensableActivity non deve essere inserito in un gestore di un altro CompensableActivity. È responsabilità di un genitore CompensableActivity garantire che, quando viene annullata, confermata o compensata, tutte le attività figlio compensabili che sono state completate con successo e non sono già state confermate o compensate, debbano essere confermate o compensate prima che il genitore completi l'annullamento, la conferma o la compensazione. Se non viene modellato esplicitamente, il CompensableActivity genitore potrà compensare in modo implicito le attività figlio compensabili nel caso in cui abbia ricevuto il segnale di annullamento o compensazione. Se il genitore ha ricevuto il segnale di conferma, confermerà in modo implicito le attività figlio compensabili. Se la logica per gestire l'annullamento, la conferma o la compensazione viene modellata in modo esplicito nel gestore del CompensableActivitypadre, qualsiasi elemento figlio non gestito in modo esplicito verrà confermato in modo implicito.