Sdílet prostřednictvím


Modelování chování zrušení v pracovních postupech

Aktivity mohou být zrušeny uvnitř pracovního postupu, například Parallel zrušením neúplných větví aktivity při CompletionCondition vyhodnocování truenebo mimo pracovní postup, pokud hostitel volá Cancel. K zajištění zpracování zrušení mohou autoři pracovního postupu používat CancellationScope aktivitu, aktivitu nebo vytvářet vlastní aktivity, které poskytují logiku CompensableActivity zrušení. Toto téma obsahuje přehled zrušení v pracovních postupech.

Zrušení, kompenzace a transakce

Transakce umožňují vaší aplikaci přerušit (vrátit zpět) všechny změny provedené v rámci transakce, pokud dojde k nějakým chybám během libovolné části procesu transakce. Ne všechna práce, která může být potřeba zrušit nebo vrátit zpět, je však vhodná pro transakce, jako je dlouhotrvající práce nebo práce, která nezahrnuje transakční prostředky. Kompenzace poskytuje model pro vrácení dříve dokončené neaktuální práce, pokud dojde k následné chybě pracovního postupu. Zrušení poskytuje model autorům pracovních postupů a aktivit pro zpracování neaktuální práce, která nebyla dokončena. Pokud aktivita nedokončila spuštění a zruší se, vyvolá se její logika zrušení, pokud je k dispozici.

Poznámka:

Další informace o transakcích a kompenzacích naleznete v tématu Transakce a kompenzace.

Použití CancellationScope

Aktivita CancellationScope má dvě části, které mohou obsahovat podřízené aktivity: Body a CancellationHandler. Tam Body se nacházejí aktivity, které tvoří logiku aktivity, a CancellationHandler tam se nacházejí aktivity, které poskytují logiku zrušení aktivity. Aktivitu je možné zrušit pouze v případě, že se nedokončila. V případě CancellationScope aktivity se dokončení týká dokončení činností v sadě Body. Pokud je naplánovaná žádost o zrušení a aktivity, které Body nebyly dokončeny, CancellationScope označí se jako Canceled a CancellationHandler aktivity se spustí.

Zrušení pracovního postupu z hostitele

Hostitel může pracovní postup zrušit voláním Cancel metody WorkflowApplication instance, která je hostitelem pracovního postupu. V následujícím příkladu je vytvořen pracovní postup, který má .CancellationScope Pracovní postup se vyvolá a hostitel zavolá Cancel. Hlavní spuštění pracovního postupu je zastaveno, CancellationHandler vyvolá se vyvolána CancellationScope a pracovní postup se dokončí se stavem 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();

Při vyvolání tohoto pracovního postupu se v konzole zobrazí následující výstup.

Spuštění pracovního postupu
Vyvolána obslužná rutina zrušení.Pracovní postup b30ebb30-df46-4d90-a211-e31c38d8db3c Canceled.

Poznámka:

CancellationScope Když je aktivita zrušena a CancellationHandler vyvolána, je odpovědností autora pracovního postupu určit průběh zrušené aktivity před zrušením, aby poskytl odpovídající logiku zrušení. Neposkytuje CancellationHandler žádné informace o průběhu zrušené aktivity.

Pracovní postup lze také zrušit z hostitele, pokud neošetřená výjimka bubliny za kořen pracovního postupu a obslužná rutina OnUnhandledException vrátí Cancel. V tomto příkladu se pracovní postup spustí a pak vyvolá .ApplicationException Tato výjimka není zpracovaná pracovním postupem, a proto je vyvolána obslužná rutina OnUnhandledException . Obslužná rutina dává modulu runtime pokyn k zrušení pracovního postupu a CancellationHandler vyvolá se aktuálně spuštěná CancellationScope aktivita.

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

Při vyvolání tohoto pracovního postupu se v konzole zobrazí následující výstup.

Spuštění pracovního postupu
OnUnhandledException in Workflow 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9An ApplicationException byl vyvolán.Vyvolána obslužná rutina zrušení.Pracovní postup 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9 Zrušeno.

Zrušení aktivity z pracovního postupu

Aktivitu může zrušit také její nadřazený objekt. Pokud Parallel má například aktivita více spuštěných větví a jeho CompletionCondition vyhodnocení true se pak zruší jeho neúplné větve. V tomto příkladu Parallel se vytvoří aktivita, která má dvě větve. Jeho CompletionCondition hodnota je nastavená tak true , aby se Parallel dokončila, jakmile se dokončí jakákoli z jejích větví. V tomto příkladu se větev 2 dokončí a větev 1 se zruší.

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

Při vyvolání tohoto pracovního postupu se v konzole zobrazí následující výstup.

Větev 1 začíná.
Větev 2 je hotová.Větev 1 byla zrušena.Pracovní postup e0685e24-18ef-4a47-acf3-5c638732f3be Dokončeno. Aktivity se také zruší, pokud se v pracovním postupu vytvoří bublina výjimky nad kořenem aktivity, ale zpracovává se na vyšší úrovni pracovního postupu. V tomto příkladu se hlavní logika pracovního postupu skládá z Sequence aktivity. Určuje Sequence se jako Body CancellationScope aktivita, která je obsažena aktivitou TryCatch . Výjimka je vyvolána z těla objektu Sequence, je zpracována nadřazenou TryCatch aktivitou a Sequence je zrušena.

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

Při vyvolání tohoto pracovního postupu se v konzole zobrazí následující výstup.

Sekvencování začíná.
Sekvence byla zrušena.Došlo k výjimce.Pracovní postup e3c18939-121e-4c43-af1c-ba1ce977ce55 Dokončeno.

Vyvolání výjimek z obslužné rutiny zrušení

Všechny výjimky vyvolané z CancellationHandler pracovního CancellationScope postupu jsou pro pracovní postup závažné. Pokud existuje možnost, že výjimky uniknou z CancellationHandler, použijte v objektu TryCatch in the CancellationHandler catch a handle tyto výjimky.

Zrušení pomocí funkce CompensableActivity

Stejně jako aktivita CancellationScopeCompensableActivity CancellationHandler. Pokud je zrušena CompensableActivity , jsou vyvolány všechny aktivity v jeho CancellationHandler . To může být užitečné pro vrácení částečně dokončené dokončené práce. Informace o tom, jak použít CompensableActivity pro kompenzaci a zrušení, naleznete v tématu Kompenzace.

Zrušení pomocí vlastních aktivit

Autoři vlastních aktivit mohou do vlastních aktivit implementovat logiku zrušení několika různými způsoby. Vlastní aktivity odvozené z Activity můžou implementovat logiku zrušení umístěním nebo jinou CancellationScope vlastní aktivitou, která obsahuje logiku zrušení do těla aktivity. AsyncCodeActivity a NativeActivity odvozené aktivity mohou přepsat příslušnou Cancel metodu a poskytnout tam logiku zrušení. CodeActivity odvozené aktivity neposkytují žádné zřizování pro zrušení, protože veškerá jejich práce se provádí v jediném nárůstu provádění při volání Execute metody runtime. Pokud metoda execute ještě nebyla volána a založená CodeActivity aktivita je zrušena, aktivita se zavře se stavem Canceled a Execute metoda není volána.

Zrušení pomocí NativeActivity

NativeActivity odvozené aktivity mohou přepsat metodu Cancel tak, aby poskytovala vlastní logiku zrušení. Pokud tato metoda není přepsána, použije se výchozí logika zrušení pracovního postupu. Výchozí zrušení je proces, ke kterému dochází, NativeActivity který nepřepíše metodu Cancel nebo jejíž Cancel metoda volá základní NativeActivity Cancel metodu. Když je aktivita zrušena, modul runtime označí aktivitu příznakem zrušení a automaticky zpracuje určité vyčištění. Pokud má aktivita pouze nezaplacené záložky, záložky budou odebrány a aktivita bude označena jako Canceled. Všechny nezaplacené podřízené aktivity zrušené aktivity budou zrušeny. Při každém pokusu o naplánování dalších podřízených aktivit dojde k ignorování pokusu a aktivita bude označena jako Canceled. Pokud se nějaká nevyrovnaná podřízená aktivita dokončí v Canceled daném nebo Faulted stavu, označí se tato aktivita jako Canceled. Všimněte si, že žádost o zrušení je možné ignorovat. Pokud aktivita nemá žádné nevyhrazené záložky ani provádění podřízených aktivit a neplánuje žádné další pracovní položky po označení příznakem zrušení, úspěšně se dokončí. Toto výchozí zrušení stačí pro mnoho scénářů, ale pokud je potřeba další logika zrušení, můžete použít předdefinované aktivity zrušení nebo vlastní aktivity.

V následujícím příkladu Cancel je definováno přepsání NativeActivity založené vlastní ParallelForEach aktivity. Když je aktivita zrušena, toto přepsání zpracovává logiku zrušení aktivity. Tento příklad je součástí ukázky Non-Generic ParallelForEach .

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

NativeActivity odvozené aktivity mohou určit, zda zrušení bylo požadováno kontrolou IsCancellationRequested vlastnosti, a označit se jako zrušené voláním MarkCanceled metody. Volání MarkCanceled okamžitě nedokon čte aktivitu. Modul runtime obvykle dokončí aktivitu, pokud nemá další nevyrovnanou práci, ale pokud MarkCanceled se nazývá konečný stav, bude místo Canceled Closed.

Zrušení pomocí AsyncCodeActivity

AsyncCodeActivity Aktivity založené na základě můžou také poskytovat vlastní logiku Cancel zrušení přepsáním metody. Pokud tato metoda není přepsána, neprovádí se žádné zpracování zrušení, pokud je aktivita zrušena. V následujícím příkladu Cancel je definováno přepsání AsyncCodeActivity založené vlastní ExecutePowerShell aktivity. Když je aktivita zrušena, provede požadované chování zrušení.

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

AsyncCodeActivity odvozené aktivity mohou určit, zda zrušení bylo požadováno kontrolou IsCancellationRequested vlastnosti, a označit se jako zrušené voláním MarkCanceled metody.