共用方式為


工作流程中取消行為的模型化

活動可以在工作流程內部取消,例如,由 Parallel 活動在它的 CompletionCondition 評估為 true 時來取消不完整的分支,或是從工作流程外部取消 (如果主機呼叫 Cancel)。 若要提供取消處理,工作流程作者可以使用 CancellationScope 活動、CompensableActivity 活動或是建立可提供取消邏輯的自訂活動。 本主題提供關於工作流程取消的概述。

取消、補償和異動

透過交易,您的應用程式能在交易過程的任何部分發生錯誤時中止(回復)該交易內執行的所有變更。 但是,並非所有可能需要取消或復原的工作都適合用於交易,例如長時間執行的工作或不涉及交易資源的工作。 補償機制提供一個模型,以在工作流程中發生後續失敗時,復原之前完成的非交易性任務。 取消會提供一個模型給工作流程和活動的作者使用,以便處理尚未完成的非交易性工作。 如果活動尚未完成執行而且遭到取消,則會叫用它的取消邏輯 (如果有的話)。

注意

如需交易和補償的詳細資訊,請參閱異動補償

使用 CancellationScope

CancellationScope 活動有兩個可以包含子活動的區段:BodyCancellationHandlerBody (組成活動邏輯的活動所在的地方) 以及 CancellationHandler (可提供活動取消邏輯之活動所在的地方)。 只有當活動尚未完成時才可以將它取消。 如果是 CancellationScope 活動,完成指的是 Body 中的活動完成。 若排定取消要求且Body中的活動尚未完成,則CancellationScope將標示為Canceled並執行CancellationHandler活動。

從主機取消工作流程

主機可以取消工作流程,其方式是呼叫正在裝載工作流程之 Cancel 執行個體的 WorkflowApplication 方法。 在下列範例中,將會建立一個具有 CancellationScope 的工作流程。 這個工作流程已被叫用,然後主機會呼叫 Cancel。 工作流程的主要執行會停止,並叫用 CancellationHandlerCancellationScope,然後工作流程會完成且狀態為 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 {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");
    }
};

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

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

wfApp.Cancel();

當叫用這個工作流程時,主控台就會顯示下列輸出。

正在啟動工作流程。
已叫用 CancellationHandler。工作流 b30ebb30-df46-4d90-a211-e31c38d8db3c 已取消。

注意

當取消 CancellationScope 活動並叫用 CancellationHandler 時,工作流程作者必須判斷此活動取消之前所完成的進度,才能提供適當的取消邏輯。 CancellationHandler 不會提供有關取消之活動進度的任何資訊。

從主機取消工作流程也可以在未處理的異常冒泡超過工作流程的根並且 OnUnhandledException 處理常式返回 Cancel 時進行。 在這個範例中,工作流程會啟動然後擲回 ApplicationException。 工作流程未處理這個例外狀況,所以叫用了 OnUnhandledException 處理常式。 此處理常式會指示執行階段取消工作流程,而且會叫用目前執行之 CancellationHandler 活動的 CancellationScope

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 {e.InstanceId}\n{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 {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");
    }
};

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

當叫用這個工作流程時,主控台就會顯示下列輸出。

正在啟動工作流程。
工作流程 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9 中的 OnUnhandledException已拋出 ApplicationException。已調用 CancellationHandler。已取消工作流程 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9。

從工作流程內部取消活動

活動的父系也可以取消該活動。 例如,如果 Parallel 活動有多個執行中的分支,並且當它的 CompletionCondition 評估為 true 時,其不完整的分支將會被取消。 在這個範例中,將會建立具有兩個分支的 Parallel 活動。 它的 CompletionCondition 會設定為 true,所以一旦它的任何一個分支完成時,Parallel 就會完成。 在這個範例中,分支 2 完成,所以分支 1 遭到取消。

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 {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");
    }
};

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

當叫用這個工作流程時,主控台就會顯示下列輸出。

分支 1 開始啟動。
分支 2 完成。分支 1 已取消。工作流程 e0685e24-18ef-4a47-acf3-5c638732f3be 已完成。如果例外狀況冒出超過活動的根源,但在工作流程的更高層處理,則活動也會被取消。 在這個範例中,工作流程的主要邏輯是由 Sequence 活動所組成。 Sequence 被指定為 Body 活動的 CancellationScope,該Body活動包含在活動中。 從 Sequence 的主體拋出例外狀況,並由父活動 TryCatch 處理,接著取消 Sequence

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 {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");
    }
};

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

當叫用這個工作流程時,主控台就會顯示下列輸出。

序列啟動中。
已取消序列。發現例外狀況。已完成工作流程 e3c18939-121e-4c43-af1c-ba1ce977ce55。

從 CancellationHandler 拋出例外狀況。

CancellationHandlerCancellationScope 擲回的任何例外狀況會對工作流程造成致命的影響。 如果例外狀況有可能從 CancellationHandler 逸出,請在 TryCatch 中使用 CancellationHandler 來攔截及處理這些例外狀況。

使用 CompensableActivity 進行取消

就像 CancellationScope 活動一樣,CompensableActivity 具有 CancellationHandler。 如果取消了 CompensableActivity,將會叫用其 CancellationHandler 中的任何活動。 這對於撤消部分完成的可補償工作很有用處。 如需如何將 CompensableActivity 用於補償和取消的資訊,請參閱補償

自訂活動的取消

自訂活動作者可以透過幾個方法,將取消邏輯實作到自訂活動中。 從 Activity 衍生的自訂活動可以實作取消邏輯,方法是將 CancellationScope 或其他包含取消邏輯的自訂活動放在活動的主體內。 AsyncCodeActivityNativeActivity 衍生活動可以覆寫各自的 Cancel 方法,並在此提供取消邏輯。 CodeActivity 衍生活動不提供任何取消的機制,因為其所有工作都是在執行階段呼叫 Execute 方法時,在單次執行中完成。 如果尚未呼叫執行方法,而且取消了根據 CodeActivity 的活動,此活動會關閉且狀態為 Canceled,而且不會呼叫 Execute 方法。

使用「NativeActivity」取消

NativeActivity 衍生活動可以覆寫 Cancel 方法來提供自訂取消邏輯。 如果此方法未被覆蓋,則將套用預設的工作流程取消邏輯。 預設取消是針對 NativeActivity 所發生的程序,此程序不會覆寫 Cancel 方法,或是此程序的 Cancel 方法會呼叫基底 NativeActivityCancel 方法。 當取消某個活動時,執行階段會將此活動標示為將要取消,而且會自動處理某些清除工作。 如果此活動只有未完成的書籤,這些書籤將會被移除,而且此活動將會標示為 Canceled。 已取消活動的任何未完成子活動將一併被取消。 任何嘗試排定額外子活動的行動都將被忽略,且該活動將被標示為Canceled。 如果有任何未完成的子活動在 CanceledFaulted 狀態下完成,那麼該活動會被標示為 Canceled。 請注意,可以忽略取消要求。 如果某個活動沒有任何未完成的書籤或是執行中的子活動,且在被標示為取消後也沒有排定任何其他工作項目,此活動將會順利完成。 在許多案例中,這個預設取消作業就足夠了,但是如果需要其他取消邏輯,則可以使用內建取消活動或自訂活動。

在下列範例中,定義了以Cancel為基礎的自訂NativeActivity活動的ParallelForEach覆寫。 當活動被取消時,此覆寫方法負責處理活動的取消邏輯。 此範例是非泛型 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 衍生活動可以檢查 IsCancellationRequested 屬性來判斷是否已經要求取消,並呼叫 MarkCanceled 方法來將其本身標示為已取消。 呼叫 MarkCanceled 不會立即完成活動。 通常執行階段會在沒有其他待處理工作時完成此活動,但是如果呼叫 MarkCanceled,最終的狀態將會是 Canceled 而不是 Closed

使用 AsyncCodeActivity 進行取消

AsyncCodeActivity 為基礎的活動也可以藉由重寫 Cancel 方法來提供自訂取消邏輯。 如果此方法未被覆寫,活動取消時不會執行任何取消處理。 在以下範例中,將會定義一個基於 Cancel 的自訂 AsyncCodeActivity 活動的 ExecutePowerShell 覆寫。 當取消此活動時,它會執行所要的取消行為。

// 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 衍生活動可以檢查 IsCancellationRequested 屬性來判斷是否已經要求取消,並呼叫 MarkCanceled 方法來將其本身標示為已取消。