工作流程中的模型化取消行為
活動可以在工作流程內部取消,例如,由 Parallel 活動在它的 CompletionCondition 評估為 true
時來取消不完整的分支,或是從工作流程外部取消 (如果主機呼叫 Cancel)。 若要提供取消處理,工作流程作者可以使用 CancellationScope 活動、CompensableActivity 活動或是建立可提供取消邏輯的自訂活動。 本主題提供工作流程取消的概觀。
取消、補償和異動
如果交易程序的任何部分發生錯誤,交易可讓應用程式中止 (回復) 在交易內部執行的所有變更。 但是,並非所有可能需要取消或復原的工作都適合異動使用,例如長時間執行的工作或是未牽涉到異動資源的工作。 補償會提供一個模型,可在工作流程發生後續失敗時,復原之前完成的非交易式工作。 取消會提供一個模型給工作流程和活動的作者使用,以便處理尚未完成的非異動式工作。 如果活動尚未完成執行而且遭到取消,則會叫用它的取消邏輯 (如果有的話)。
使用 CancellationScope
CancellationScope 活動有兩個可以包含子活動的區段:Body 和 CancellationHandler。 Body (組成活動邏輯的活動所在的地方) 以及 CancellationHandler (可提供活動取消邏輯之活動所在的地方)。 只有當活動尚未完成時才可以將它取消。 如果是 CancellationScope 活動,完成指的是 Body 中的活動完成。 如果排定了取消要求而且尚未完成 Body 中的活動,則 CancellationScope 將會標示為 Canceled 而且將會執行 CancellationHandler 活動。
從主機取消工作流程
主機可以取消工作流程,其方式是呼叫正在裝載工作流程之 Cancel 執行個體的 WorkflowApplication 方法。 在下列範例中,將會建立一個具有 CancellationScope 的工作流程。 將會叫用此工作流程,然後主機會呼叫 Cancel。 工作流程的主要執行工作會停止,並叫用 CancellationHandler 的 CancellationScope,然後工作流程會完成而且狀態為 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();
當叫用這個工作流程時,主控台就會顯示下列輸出。
正在啟動工作流程。
已叫用 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 {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();
當叫用這個工作流程時,主控台就會顯示下列輸出。
正在啟動工作流程。
工作流程 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 {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();
當叫用這個工作流程時,主控台就會顯示下列輸出。
分支 1 正在啟動。
分支 2 完成。已取消分支 1。已完成工作流程 e0685e24-18ef-4a47-acf3-5c638732f3be。如果例外狀況反昇而超過活動的根,但是在工作流程的更高層處理,也會取消活動。 在這個範例中,工作流程的主要邏輯是由 Sequence 活動所組成。 Sequence 會指定為 Body 活動的 CancellationScope,該活動包含在 TryCatch 活動中。 從 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 {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();
當叫用這個工作流程時,主控台就會顯示下列輸出。
正在啟動序列。
已取消序列。發現例外狀況。已完成工作流程 e3c18939-121e-4c43-af1c-ba1ce977ce55。
從 CancellationHandler 擲回例外狀況。
從 CancellationHandler 的 CancellationScope 擲回的任何例外狀況對工作流程都很重要。 如果例外狀況有可能從 CancellationHandler 逸出,請在 TryCatch 中使用 CancellationHandler 來攔截及處理這些例外狀況。
使用 CompensableActivity 取消
就像 CancellationScope 活動一樣,CompensableActivity 具有 CancellationHandler。 如果取消了 CompensableActivity,將會叫用其 CancellationHandler 中的任何活動。 這對於復原部分完成的可補償工作很有用處。 如需如何將 CompensableActivity 用於補償和取消的資訊,請參閱補償。
使用自訂活動取消
自訂活動作者可以透過幾個方法,將取消邏輯實作到自訂活動中。 從 Activity 衍生的自訂活動可以實作取消邏輯,方法是將 CancellationScope 或其他包含取消邏輯的自訂活動放在活動的主體內。 AsyncCodeActivity 和 NativeActivity 衍生活動可以覆寫其各自的 Cancel 方法,並在這裡提供取消邏輯。 CodeActivity 衍生活動不會提供任何取消的規定,因為其所有工作都是在執行階段呼叫 Execute 方法時,於單一執行中執行。 如果尚未呼叫執行方法,而且取消了根據 CodeActivity 的活動,此活動會關閉且狀態為 Canceled,而且不會呼叫 Execute 方法。
使用 NativeActivity 取消
NativeActivity 衍生活動可以覆寫 Cancel 方法來提供自訂取消邏輯。 如果這個方法未遭到覆寫,則會套用預設工作流程取消邏輯。 預設取消是針對 NativeActivity 所發生的程序,此程序不會覆寫 Cancel 方法,或是此程序的 Cancel 方法會呼叫基底 NativeActivity Cancel 方法。 當取消某個活動時,執行階段會將此活動標示為將要取消,而且會自動處理某些清除工作。 如果此活動只有待處理的書籤,這些書籤將會被移除,而且此活動將會標示為 Canceled。 接著會取消已取消之活動的任何待處理的子活動。 嘗試排定其他子活動的任何動作都會遭到忽略,而且此活動會標示為 Canceled。 如果有任何待處理的子活動在 Canceled 或 Faulted 狀態下完成,此活動會標示為 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 方法來將其本身標示為已取消。