Modellazione del comportamento di annullamento nei flussi di lavoroModeling Cancellation Behavior in Workflows

Le attività possono essere annullate all'interno di un flusso di lavoro, ad esempio da un'attività Parallel che annulla i rami incompleti quando la proprietà CompletionCondition restituisce true oppure all'esterno del flusso di lavoro, se l'host chiama il metodo Cancel.Activities can be canceled inside a workflow, for example by a Parallel activity canceling incomplete branches when its CompletionCondition evaluates to true, or from outside the workflow, if the host calls Cancel. Per assicurare la gestione dell'annullamento, gli autori del flusso di lavoro possono usare le attività CancellationScope e CompensableActivity o creare attività personalizzate che forniscono la logica di annullamento.To provide cancellation handling, workflow authors can use the CancellationScope activity, the CompensableActivity activity, or create custom activities that provide cancellation logic. In questo argomento viene fornita una panoramica sull'annullamento nei flussi di lavoro.This topic provides an overview of cancellation in workflows.

Annullamento, compensazione e transazioniCancellation, Compensation, and Transactions

Le transazioni consentono all'applicazione di interrompere, ovvero eseguire il rollback, tutte le modifiche eseguite all'interno della transazione se si verificano errori durante qualsiasi parte del processo di transazione.Transactions give your application the ability to abort (roll back) all changes executed within the transaction if any errors occur during any part of the transaction process. Tuttavia, non tutte le operazioni che potrebbero dover essere annullate sono appropriate per le transazioni, ad esempio operazioni con esecuzione prolungata o che non implicano risorse transazionali.However, not all work that may need to be canceled or undone is appropriate for transactions, such as long-running work or work that does not involve transactional resources. La compensazione fornisce un modello per annullare operazioni non transazionali completate precedentemente se, in seguito, si verifica un errore nel flusso di lavoro.Compensation provides a model for undoing previously completed non-transactional work if there is a subsequent failure in the workflow. L'annullamento fornisce agli autori di flussi di lavoro e attività un modello per gestire le operazioni non transazionali che non sono state completate.Cancellation provides a model for workflow and activity authors to handle non-transactional work that was not completed. Se l'attività viene annullata poiché non ne è stata completata l'esecuzione, sarà richiamata la relativa logica di annullamento, se disponibile.If an activity has not completed its execution and it is canceled, its cancellation logic will be invoked if it is available.

Nota

Per ulteriori informazioni sulle transazioni e compensazione, vedere transazioni e compensazione.For more information about transactions and compensation, see Transactions and Compensation.

Utilizzo dell'attività CancellationScopeUsing CancellationScope

L'attività CancellationScope dispone di due sezioni che possono contenere le attività figlio Body e CancellationHandler.The CancellationScope activity has two sections that can contain child activities: Body and CancellationHandler. Nella proprietà Body vengono posizionate le attività che costituiscono la logica dell'attività e nella proprietà CancellationHandler vengono posizionate le attività che forniscono la logica di annullamento per l'attività.The Body is where the activities that make up the logic of the activity are placed, and the CancellationHandler is where the activities that provide cancellation logic for the activity are placed. Un'attività può essere annullata solo se non è stata completata.An activity can be canceled only if it has not completed. Nel caso dell'attività CancellationScope, con completamento si intende il completamento delle attività nella proprietà Body.In the case of the CancellationScope activity, completion refers to the completion of the activities in the Body. Se viene pianificata una richiesta di annullamento e le attività nella proprietà Body non sono state completate, l'attività CancellationScope sarà contrassegnata come Canceled e saranno eseguite le attività della proprietà CancellationHandler.If a cancellation request is scheduled and the activities in the Body have not completed, then the CancellationScope will be marked as Canceled and the CancellationHandler activities will be executed.

Annullamento di un flusso di lavoro dall'hostCanceling a Workflow from the Host

Un host può annullare un flusso di lavoro chiamando il metodo Cancel dell'istanza WorkflowApplication che sta ospitando il flusso di lavoro.A host can cancel a workflow by calling the Cancel method of the WorkflowApplication instance that is hosting the workflow. Nell'esempio seguente, viene creato un flusso di lavoro che dispone di un'attività CancellationScope.In the following example a workflow is created that has a CancellationScope. Una volta richiamato il flusso di lavoro, l'host effettua una chiamata al metodo Cancel.The workflow is invoked, and then the host makes a call to Cancel. L'esecuzione principale del flusso di lavoro viene interrotta, viene richiamata la proprietà CancellationHandler dell'attività CancellationScope, quindi il flusso di lavoro viene completato con lo stato Canceled.The main execution of the workflow is stopped, the CancellationHandler of the CancellationScope is invoked, and then the workflow completes with a status of Canceled.

Activity wf = new CancellationScope
{
    Body = new Sequence
    {
        Activities = 
        {
            new WriteLine
            {
                Text = "Starting the workflow."
            },
            new Delay
            {
                Duration = TimeSpan.FromSeconds(5)
            },
            new WriteLine
            {
                Text = "Ending the workflow."
            }
        }
    },
    CancellationHandler = new WriteLine
    {
        Text = "CancellationHandler invoked."
    }
};

// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Subscribe to any desired workflow lifecycle events.
wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
    if (e.CompletionState == ActivityInstanceState.Faulted)
    {
        Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
        Console.WriteLine("Exception: {0}\n{1}",
            e.TerminationException.GetType().FullName,
            e.TerminationException.Message);
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
    }
    else
    {
        Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
    }
};

// Run the workflow.
wfApp.Run();

Thread.Sleep(TimeSpan.FromSeconds(1));

wfApp.Cancel();

Quando questo flusso di lavoro viene richiamato, nella console viene visualizzato l'output seguente.When this workflow is invoked, the following output is displayed to the console.

Avvio del flusso di lavoro.Starting the workflow.
Oggetto CancellationHandler richiamato. CancellationHandler invoked.
Flusso di lavoro b30ebb30-df46-4d90-a211-e31c38d8db3c annullata.Workflow b30ebb30-df46-4d90-a211-e31c38d8db3c Canceled.

Nota

Quando un'attività CancellationScope viene annullata e viene richiamata la proprietà CancellationHandler, l'autore del flusso di lavoro deve determinare lo stato dell'attività prima del relativo annullamento al fine di fornire la logica di annullamento appropriata.When a CancellationScope activity is canceled and the CancellationHandler invoked, it is the responsibility of the workflow author to determine the progress that the canceled activity made before it was canceled in order to provide the appropriate cancellation logic. La proprietà CancellationHandler non fornisce alcuna informazione sullo stato dell'attività annullata.The CancellationHandler does not provide any information about the progress of the canceled activity.

Un flusso di lavoro può essere annullato anche dall'host se un'eccezione non gestita viene propagata oltre la radice del flusso di lavoro e il gestore della proprietà OnUnhandledException restituisce Cancel.A workflow can also be canceled from the host if an unhandled exception bubbles up past the root of the workflow and the OnUnhandledException handler returns Cancel. In questo esempio, viene avviato il flusso di lavoro che, successivamente, genera un'eccezione ApplicationException.In this example the workflow starts and then throws an ApplicationException. Questa eccezione non viene gestita dal flusso di lavoro, pertanto viene richiamato il gestore della proprietà OnUnhandledException.This exception is unhandled by the workflow and so the OnUnhandledException handler is invoked. Il gestore indica al runtime di annullare il flusso di lavoro e viene richiamata la proprietà CancellationHandler dell'attività CancellationScope attualmente in corso di esecuzione.The handler instructs the runtime to cancel the workflow, and the CancellationHandler of the currently executing CancellationScope activity is invoked.

Activity wf = new CancellationScope
{
    Body = new Sequence
    {
        Activities = 
        {
            new WriteLine
            {
                Text = "Starting the workflow."
            },
            new Throw
            {
                 Exception = new InArgument<Exception>((env) => 
                     new ApplicationException("An ApplicationException was thrown."))
    
            },
            new WriteLine
            {
                Text = "Ending the workflow."
            }
        }
    },
    CancellationHandler = new WriteLine
    {
        Text = "CancellationHandler invoked."
    }
};

// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Subscribe to any desired workflow lifecycle events.
wfApp.OnUnhandledException = delegate(WorkflowApplicationUnhandledExceptionEventArgs e)
{
    // Display the unhandled exception.
    Console.WriteLine("OnUnhandledException in Workflow {0}\n{1}",
        e.InstanceId, e.UnhandledException.Message);

    // Instruct the runtime to cancel the workflow.
    return UnhandledExceptionAction.Cancel;
};

wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
    if (e.CompletionState == ActivityInstanceState.Faulted)
    {
        Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
        Console.WriteLine("Exception: {0}\n{1}",
            e.TerminationException.GetType().FullName,
            e.TerminationException.Message);
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
    }
    else
    {
        Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
    }
};

// Run the workflow.
wfApp.Run();


Quando questo flusso di lavoro viene richiamato, nella console viene visualizzato l'output seguente.When this workflow is invoked, the following output is displayed to the console.

Avvio del flusso di lavoro.Starting the workflow.
OnUnhandledException nel flusso di lavoro 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9 OnUnhandledException in Workflow 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9
È stata generata un'eccezione ApplicationException. An ApplicationException was thrown.
Oggetto CancellationHandler richiamato. CancellationHandler invoked.
Flusso di lavoro 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9 annullata.Workflow 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9 Canceled.

Annullamento di un'attività dall'interno di un flusso di lavoroCanceling an Activity from Inside a Workflow

Un'attività può essere annullata anche dal relativo elemento padre.An activity can also be canceled by its parent. Ad esempio, se un'attività Parallel dispone di più rami in esecuzione e la relativa proprietà CompletionCondition restituisce true, i rami incompleti corrispondenti saranno annullati.For example, if a Parallel activity has multiple executing branches and its CompletionCondition evaluates to true then its incomplete branches will be canceled. In questo esempio viene creata un'attività Parallel che dispone di due rami.In this example a Parallel activity is created that has two branches. La relativa proprietà CompletionCondition è impostata su true, pertanto l'oggetto Parallel viene completato non appena si verifica la stessa condizione per uno dei relativi rami.Its CompletionCondition is set to true so the Parallel completes as soon as any one of its branches is completed. In questo esempio viene completato il ramo 2, pertanto il ramo 1 viene annullato.In this example branch 2 completes, and so branch 1 is canceled.

Activity wf = new Parallel
{
    CompletionCondition = true,
    Branches = 
    {
        new CancellationScope
        {
            Body = new Sequence
            {
                Activities = 
                {
                    new WriteLine
                    {
                        Text = "Branch 1 starting."
                    },
                    new Delay
                    {
                         Duration = TimeSpan.FromSeconds(2)
                    },
                    new WriteLine
                    {
                        Text = "Branch 1 complete."
                    }
                }
            },
            CancellationHandler = new WriteLine
            {
                Text = "Branch 1 canceled."
            }
        },
        new WriteLine
        {
            Text = "Branch 2 complete."
        }
    }
};

// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);

wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
    if (e.CompletionState == ActivityInstanceState.Faulted)
    {
        Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
        Console.WriteLine("Exception: {0}\n{1}",
            e.TerminationException.GetType().FullName,
            e.TerminationException.Message);
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
    }
    else
    {
        Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
    }
};

// Run the workflow.
wfApp.Run();


Quando questo flusso di lavoro viene richiamato, nella console viene visualizzato l'output seguente.When this workflow is invoked, the following output is displayed to the console.

Ramo 1.Branch 1 starting.
Ramo 2 completato. Branch 2 complete.
Ramo 1 annullato. Branch 1 canceled.
E0685e24-18ef-4a47-acf3-5c638732f3be del flusso di lavoro completato.Workflow e0685e24-18ef-4a47-acf3-5c638732f3be Completed. Le attività vengono annullate anche se un'eccezione viene propagata oltre la radice dell'attività, ma viene gestita a un livello superiore nel flusso di lavoro.Activities are also canceled if an exception bubbles up past the root of the activity but is handled at a higher level in the workflow. In questo esempio la logica principale del flusso di lavoro è costituita da un'attività Sequence.In this example, the main logic of the workflow consists of a Sequence activity. Sequence è specificato come Body di un'attività CancellationScope che è contenuta da un'attività TryCatch.The Sequence is specified as the Body of a CancellationScope activity which is contained by a TryCatch activity. Dal corpo dell'attività Sequence viene generata un'eccezione che viene gestita dall'attività padre TryCatch. L'attività Sequence viene quindi annullata.An exception is thrown from the body of the Sequence, is handled by the parent TryCatch activity, and the Sequence is canceled.

Activity wf = new TryCatch
{
    Try = new CancellationScope
    {
        Body = new Sequence
        {
            Activities = 
            {
                new WriteLine
                {
                    Text = "Sequence starting."
                },
                new Throw
                {
                     Exception = new InArgument<Exception>((env) => 
                         new ApplicationException("An ApplicationException was thrown."))
        
                },
                new WriteLine
                {
                    Text = "Sequence complete."
                }
            }
        },
        CancellationHandler = new WriteLine
        {
            Text = "Sequence canceled."
        }
    },
    Catches =
    {
        new Catch<ApplicationException>
        {
            Action = new ActivityAction<ApplicationException>
            {
                Handler  = new WriteLine
                {
                    Text = "Exception caught."
                }
            }
        }
    }

};

// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);

wfApp.Completed = delegate(WorkflowApplicationCompletedEventArgs e)
{
    if (e.CompletionState == ActivityInstanceState.Faulted)
    {
        Console.WriteLine("Workflow {0} Terminated.", e.InstanceId);
        Console.WriteLine("Exception: {0}\n{1}",
            e.TerminationException.GetType().FullName,
            e.TerminationException.Message);
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
    }
    else
    {
        Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
    }
};

// Run the workflow.
wfApp.Run();

Quando questo flusso di lavoro viene richiamato, nella console viene visualizzato l'output seguente.When this workflow is invoked, the following output is displayed to the console.

Sequenza di avvio.Sequence starting.
Attività Sequence annullata. Sequence canceled.
Eccezione rilevata. Exception caught.
E3c18939-121e-4c43-af1c-ba1ce977ce55 del flusso di lavoro completato.Workflow e3c18939-121e-4c43-af1c-ba1ce977ce55 Completed.

Generazione di eccezioni da un oggetto CancellationHandlerThrowing Exceptions from a CancellationHandler

Qualsiasi eccezione generata dalla proprietà CancellationHandler di un'attività CancellationScope è irreversibile per il flusso di lavoro.Any exceptions thrown from the CancellationHandler of a CancellationScope are fatal to the workflow. Se è possibile che le eccezioni escano da una proprietà CancellationHandler, usare un oggetto TryCatch nella proprietà CancellationHandler per rilevare e gestire tali eccezioni.If there is a possibility of exceptions escaping from a CancellationHandler, use a TryCatch in the CancellationHandler to catch and handle these exceptions.

Annullamento tramite l'oggetto CompensableActivityCancellation using CompensableActivity

Analogamente all'attività CancellationScope, l'oggetto CompensableActivity dispone di una proprietà CancellationHandler.Like the CancellationScope activity, the CompensableActivity has a CancellationHandler. Se un oggetto CompensableActivity viene annullato, viene richiamata qualsiasi attività nella relativa proprietà CancellationHandler.If a CompensableActivity is canceled, any activities in its CancellationHandler are invoked. Tale operazione può essere utile per annullare operazioni compensabili completate parzialmente.This can be useful for undoing partially completed compensable work. Per informazioni su come usare CompensableActivity per compensazione e l'annullamento, vedere compensazione.For information about how to use CompensableActivity for compensation and cancellation, see Compensation.

Annullamento tramite attività personalizzateCancellation using Custom Activities

Gli autori di attività personalizzate possono implementare la logica di annullamento nelle proprie attività personalizzate in molti modi diversi.Custom activity authors can implement cancellation logic into their custom activities in several different ways. Le attività personalizzate che derivano da Activity possono implementare la logica di annullamento inserendo CancellationScope o un'altra attività personalizzata che contiene la logica di annullamento nel corpo dell'attività.Custom activities that derive from Activity can implement cancellation logic by placing a CancellationScope or other custom activity that contains cancellation logic in the body of the activity. Le attività derivate AsyncCodeActivity e NativeActivity possono eseguire l'override del rispettivo metodo Cancel e fornire qui la logica di annullamento.AsyncCodeActivity and NativeActivity derived activities can override their respective Cancel method and provide cancellation logic there. Le attività derivate CodeActivity non forniscono alcuna misura per l'annullamento poiché tutto il lavoro viene eseguito in un unico picco di esecuzione quando il runtime chiama il metodo Execute.CodeActivity derived activities do not provide any provision for cancellation because all their work is performed in a single burst of execution when the runtime calls the Execute method. Se il metodo Execute non è ancora stato chiamato e viene annullata un'attività basata sull'oggetto CodeActivity, l'attività viene chiusa con lo stato Canceled e il metodo Execute non viene chiamato.If the execute method has not yet been called and a CodeActivity based activity is canceled, the activity closes with a status of Canceled and the Execute method is not called.

Annullamento tramite l'oggetto NativeActivityCancellation using NativeActivity

Le classi derivate NativeActivity possono eseguire l'override del metodo Cancel per fornire la logica di annullamento personalizzata.NativeActivity derived activities can override the Cancel method to provide custom cancellation logic. Se questo metodo non viene sottoposto a override, viene applicata la logica di annullamento del flusso di lavoro predefinita.If this method is not overridden, then the default workflow cancellation logic is applied. Annullamento predefinito è il processo che si verifica per un NativeActivity non che esegue l'override di Cancel metodo o il cui Cancel metodo chiama il metodo base NativeActivity Cancel metodo.Default cancellation is the process that occurs for a NativeActivity that does not override the Cancel method or whose Cancel method calls the base NativeActivity Cancel method. Quando un'attività viene annullata, il runtime contrassegna l'attività per l'annullamento e gestisce automaticamente la pulizia.When an activity is canceled, the runtime flags the activity for cancellation and automatically handles certain cleanup. Se l'attività dispone solo di segnalibri in attesa, questi ultimi saranno rimossi e l'attività sarà contrassegnata come Canceled.If the activity only has outstanding bookmarks, the bookmarks will be removed and the activity will be marked as Canceled. Qualsiasi attività figlio in attesa dell'attività annullata sarà annullata a sua volta.Any outstanding child activities of the canceled activity will in turn be canceled. Qualsiasi tentativo di pianificare attività figlio aggiuntive verrà ignorato e l'attività sarà contrassegnata come Canceled.Any attempt to schedule additional child activities will result in the attempt being ignored and the activity will be marked as Canceled. Se qualsiasi attività figlio in attesa viene completata nello stato Canceled o Faulted, l'attività sarà contrassegnata come Canceled.If any outstanding child activity completes in the Canceled or Faulted state, then the activity will be marked as Canceled. Si noti che è possibile ignorare una richiesta di annullamento.Note that a cancellation request can be ignored. Se un'attività non dispone di segnalibri in attesa o di attività figlio in esecuzione e non pianifica alcun elemento di lavoro aggiuntivo dopo essere stata contrassegnata per l'annullamento, verrà completata correttamente.If an activity does not have any outstanding bookmarks or executing child activities and does not schedule any additional work items after being flagged for cancellation, it will complete successfully. Questo annullamento predefinito è sufficiente per molti scenari, tuttavia se è necessaria un'ulteriore logica di annullamento, è possibile usare le attività di annullamento incorporate o le attività personalizzate.This default cancellation suffices for many scenarios, but if additional cancellation logic is needed, then the built-in cancellation activities or custom activities can be used.

Nell'esempio seguente, viene definito l'override Cancel di un'attività NativeActivity personalizzata basata sull'oggetto ParallelForEach.In the following example, the Cancel override of a NativeActivity based custom ParallelForEach activity is defined. Quando l'attività viene annullata, questo override gestisce la logica di annullamento per l'attività.When the activity is canceled, this override handles the cancellation logic for the activity. In questo esempio fa parte di ParallelForEach Non generica esempio.This example is part of the Non-Generic ParallelForEach sample.

protected override void Cancel(NativeActivityContext context)  
{  
    // If we do not have a completion condition then we can just  
    // use default logic.  
    if (this.CompletionCondition == null)  
    {  
        base.Cancel(context);  
    }  
    else  
    {  
        context.CancelChildren();  
    }  
}  

Le attività derivate da NativeActivity possono determinare se l'annullamento è stato richiesto in seguito al controllo della proprietà IsCancellationRequested e possono contrassegnarsi come annullate chiamando il metodo MarkCanceled.NativeActivity derived activities can determine if cancellation has been requested by inspecting the IsCancellationRequested property, and mark themselves as canceled by calling the MarkCanceled method. La chiamata al metodo MarkCanceled non completa immediatamente l'attività.Calling MarkCanceled does not immediately complete the activity. Come al solito, il runtime completerà l'attività quando non dispone più di alcuna operazione in attesa, tuttavia se viene chiamato il metodo MarkCanceled, lo stato finale sarà Canceled anziché Closed.As usual, the runtime will complete the activity when it has no more outstanding work, but if MarkCanceled is called the final state will be Canceled instead of Closed.

Annullamento tramite l'oggetto AsyncCodeActivityCancellation using AsyncCodeActivity

Anche le attività basate sull'oggetto AsyncCodeActivity possono fornire la logica di annullamento personalizzata eseguendo l'override del metodo Cancel.AsyncCodeActivity based activities can also provide custom cancellation logic by overriding the Cancel method. Se questo metodo non viene sottoposto a override, non viene eseguita alcuna gestione dell'annullamento se l'attività viene annullata.If this method is not overridden, then no cancellation handling is performed if the activity is canceled. Nell'esempio seguente, viene definito l'override Cancel di un'attività AsyncCodeActivity personalizzata basata sull'oggetto ExecutePowerShell.In the following example, the Cancel override of an AsyncCodeActivity based custom ExecutePowerShell activity is defined. Quando l'attività viene annullata, esegue il comportamento di annullamento desiderato.When the activity is canceled, it performs the desired cancellation behavior. In questo esempio fa parte di utilizzando l'attività InvokePowerShell esempio.This example is part of the Using the InvokePowerShell Activity sample.

// Called by the runtime to cancel the execution of this asynchronous activity.
protected override void Cancel(AsyncCodeActivityContext context)
{
    Pipeline pipeline = context.UserState as Pipeline;
    if (pipeline != null)
    {
        pipeline.Stop();
        DisposePipeline(pipeline);
    }
    base.Cancel(context);
}

Le attività derivate da AsyncCodeActivity possono determinare se l'annullamento è stato richiesto in seguito al controllo della proprietà IsCancellationRequested e possono contrassegnarsi come annullate chiamando il metodo MarkCanceled.AsyncCodeActivity derived activities can determine if cancellation has been requested by inspecting the IsCancellationRequested property, and mark themselves as canceled by calling the MarkCanceled method.