本文章是由機器翻譯。

地基

在工作流程處理時發生錯誤

Matt Milner

可從 MSDN 程式庫 的程式碼下載
瀏覽線上的程式碼

內容

處理工作流程中的錯誤
處理主控件程序中的錯誤
處理錯誤的自訂活動
使用補償
重試活動

Windows Workflow Foundation (WF) 會提供定義豐富的商務程序和執行和管理這些處理序在執行階段環境工具。商務內含任何預期的執行流程的例外狀況發生時,及開發人員必須能夠撰寫穩固的應用程式邏輯從這些例外狀況復原]。大部分的任何技術的範例,往往忽略錯誤處理和適當的修復。在這個月的我將示範如何處理正確地在數個的層級,WF 程式設計模型的例外狀況,以及如何建置豐富的例外處理到您的工作流程和主機的功能。

處理工作流程中的錯誤

開發人員建置商務處理流程必須能夠處理商務實例,以確保處理序本身是可回復性的例外狀況,並且可以在發生失敗後繼續。這點特別重要的工作流程,它們通常會定義長時間執行的處理序,並在大部分的情況下發生未處理的錯誤,表示需要重新啟動處理序。工作流程,預設行為是,如果在工作流程中發生例外狀況,且未處理,即將終止工作流程。因此,它是重要工作流程開發人員正確範圍的工作、 處理錯誤,和建置,工作流程,能夠重試失敗發生時的工作。

處理錯誤,工作流程中的有許多與處理錯誤,在 Microsoft.NET Framework 為目標的程式碼和一些新的概念。若要處理錯誤,第一個步驟是定義執行的範圍。.NET 的程式碼這被完成使用 try 關鍵字。在工作流程,大部分複合活動可以用來建立例外處理的範圍。每個複合的活動會有主要的檢視顯示子活動,並也有替代檢視。[圖 1 ] 中的上一個 Sequence 活動內容功能表會顯示可以存取的方式不同的檢視] 和 [選取 [檢視錯誤處理常式] 選項的結果。

fig01.gif

[圖 1 替代檢視功能表] 和 [SelectingView 錯誤處理常式

切換至錯誤處理常式檢視時, 則會將一個 FaultHandlers 活動加入至序列活動的活動集合。FaultHandlers 活動中, 可以加入個別的 FaultHandler 活動。每一個 FaultHandler 活動有一個屬性定義錯誤型別,和就像在.NET 運算式攔截。

FaultHandler 活動將是您,複合活動,可讓工作流程開發人員加入定義如何處理的例外狀況的子活動。這些活動,可以提供記錄錯誤,請連絡系統管理員或任何其他您通常可以採用處理您的程式碼中的例外狀況時的動作的功能。

FaultHandler 活動也會具有錯誤屬性,包含被攔截的例外狀況。子活動可以取得例外狀況的存取繫結至這個屬性。[圖 2 ,自訂的記錄活動有 FaultHandler 活動繫結至錯誤屬性的例外狀況屬性的其中所示。記錄活動可以現在例外狀況資訊寫入一個記錄 API 」、 「 Windows 事件記錄檔 」、 「 Windows Management Instrumentation (WMI) 或 「 任何其他目的。

fig02.gif

[圖 2 繫結至錯誤

像 catch 區塊,FaultHandler 活動會評估根據其錯誤型別。定義工作流程時, FaultHandler 活動應該要加入至 [FaultHandlers 順序從最特定至最不特定,錯誤由左至右。

fig03.gif

[圖 3 執行之後,複合的延續

當例外狀況,就會發生而且在 catch 區塊完成執行之後,在.NET 的程式碼中攔截到例外狀況會繼續嘗試範圍之後。所以在一個的工作流程會執行繼續下一個活動之後,會在處理例外狀況的複合活動 (請參閱 [圖 3 )。

有兩個金鑰概念 FaultHandler 活動會評估並執行。Throw 活動 (為在 [圖 3 ),所執行,或另一個活動擲回例外狀況時, 執行階段會讓活動不在的狀態,並 HandleFault 方法執行活動的排程。我會進入詳細的實如何活動作這個方法很快就,但現在它是足以知道這是活動,來清除的機會。

當活動完成清理,並移至已關閉的狀態時,父活動就會放入錯誤的狀態,然後是也提供機會,清除任何子活動來表示它已準備好要移至已關閉的狀態。它是這個時候準備好要移至已關閉的狀態,執行階段會檢查狀態發出信號的複合活動時,時 it is 失敗,檢查 FaultHandlers 集合。如果符合目前的例外狀況的錯誤類型找不到一個 FaultHandler 活動,則請) 排定在該 FaultHandler,評估已中止。一旦在 FaultHandler 關閉下, 一個活動可以再繼續執行。

當例外狀況發生時,Runtime 會嘗試尋找直接上層父代的複合活動的容錯的處理活動。如果找不不到任何相符的處理常式錯誤的複合和例外狀況 Bubbles 到下一個複合活動在樹狀目錄中。這很類似如何泡泡圖的.NET 例外狀況上呼叫的方法堆疊時發生未處理的。如果例外狀況會一直 Bubbles 則根目錄找不到沒有處理常式的工作流程活動,則工作流程已終止。

請注意工作流程本身是複合的活動,因此,可以具有定義來處理例外狀況,以達到最上層的容錯的處理邏輯。這是最後的機會在工作流程開發人員,攔截和處理商務程序中的例外狀況。

處理主控件程序中的錯誤

雖然建立強固的工作流程是很重要,需要主應用程式可以處理的例外狀況是您的應用程式的穩定性同樣重要的。幸好,工作流程執行階段是在處理這些例外狀況,現成的強大,並保護主機處理序從反昇 (Event Bubbling) 上大部分的例外狀況。

在工作流程反昇設定透過在的階層架構中發生例外狀況,並發生無法攔截時, 工作流程將會終止,並在 Runtime 引發的事件。執行階段主應用程式可以註冊處理常式以這些例外狀況發生時,但例外狀況不會導致損毀主應用程式時收到通知。取得收到的通知這些終止,主機處理序可以使用類似下列的程式碼從事件引數取得例外狀況的相關資訊:

workflowRuntime.WorkflowTerminated += delegate(
  object sender, WorkflowTerminatedEventArgs e)
{
  Console.WriteLine(e.Exception.Message);
};

除了結束的工作流程的處理主機處理序也能夠獲得通知關於執行階段服務中發生的例外狀況。 例如,如果 SqlWorkflowPersistence­service 載入至執行階段,它會輪詢資料庫,並可能會嘗試定期會在能進一步進行工作時,載入工作流程。 當您嘗試載入工作流程時, 持續性服務可能例外狀況嘗試還原序列化例如的工作流程時。 發生這種情況時很再次重要的主機處理序不會失敗,這是這些服務不重新擲回這些例外狀況的原因。 而,它們會引發工作流程執行階段事件。 Runtime 接著會引發 ServicesExceptionNotHandled 事件可在程式碼,處理,如下所示:

workflowRuntime.ServicesExceptionNotHandled += delegate(
  object sender, ServicesExceptionNotHandledEventArgs snhe)
{
  Console.WriteLine(snhe.Exception.Message);
};

一般來說,執行階段服務的開發人員必須選擇時攔截例外狀況有關例外狀況是否重要。 SqlWorkflowPersistenceService 中, 無法載入一個單一的工作流程並不表示服務無法運作。 因此,合理在這種情況下直接引發的事件讓主應用程式處理序,以判斷是否進一步動作需要。 不過,如果持續性服務無法連線到 SQL Server 資料庫,再它無法運作完全。 在這種情況下,而不是引發的事件,合理多個服務,以擲回例外狀況,並使中止主應用程式,以便可以解決這個問題。

開發自訂的執行階段服務時, 建議的方法是衍生自 WorkflowRuntimeService 基底類別的這些服務。 這個基底類別會提供存取至執行階段和一個受保護的方法,來引發 ServicesExceptionNotHandled 事件。 在執行階段服務的執行發生例外狀況時, 服務應該只例外,如果真的是無法復原的錯誤。 如果錯誤與單一的工作流程執行個體] 和 [不,一般執行服務的相關,再為應該引發事件而。

處理錯誤的自訂活動

活動的作者的例外處理會略有不同的意義。 在活動中處理的例外狀況目標是兩個: 處理反昇 (Event Bubbling) 設定和中斷的工作流程中保留它們時可能,並在其中為 Bubbles 出活動的未處理的例外狀況的情況下正確地清除發生的例外狀況。

因為活動類別只處理活動內的例外狀況.與任何其他類別不不同。 當 try / catch 區塊時,可以使用呼叫其他元件,可能會擲回錯誤。 不過,一旦您在活動中攔截例外狀況,您必須決定是否要重新擲回例外狀況。 如果例外狀況,並不會影響在活動中或您的活動的結果有更控制方式表示它是在未成功]、 [這是提供的意見反應,慣用的方法。 不過,例外狀況意味著您的活動失敗並無法完成處理或提供的指示,在的失敗的然後您應該擲回例外狀況,如果工作流程開發人員可以設計商業處理序處理的例外狀況。

其他 Facet 的活動中處理例外狀況會處理清除活動的資源。 不同於在一個工作流程的錯誤處理著重於在商務程序、 記錄,與通知,處理錯誤的活動是主要被著重清除資源,使用活動執行。

您在處理錯誤的方式也會取決您正在撰寫一個分葉活動或複合活動。 在一個的分葉活動 HandleFault 方法稱為未處理的例外狀況會攔截到由執行階段,為了讓活動來釋放任何資源,可能會被使用中,並清除任何已開始執行時。 例如,如果活動在執行期間,使用資料庫,在 HandleFault 方法中它應該確定關閉連接,如果它不是已關閉,並可能在使用的任何其他資源處置。 如果活動已開始任何非同步工作,這就將時間,[取消],工作,並釋放所使用的處理資源。

在複合的活動 HandleFault 方法時它可能會因為本身,活動中邏輯錯誤,或可能因為子活動已失敗。 不論是哪的種情況,在複合活動上呼叫 HandleFault 方法的用意是讓活動,來清除其子活動。 這個清除會涉及確定複合不要求任何更多的活動會執行,以及取消正在執行的任何活動。 幸運的是方法的,在 HandleFault,composite­activity 基底類別中定義的預設實作是方法的複合活動呼叫 Cancel 方法。

取消將是您,另一個機制,可讓非同步工作已啟動某些的活動,及目前正在等候通知它們應該取消它們已啟動,和清除其資源,所以它們可以關閉的工作完成的工作。 活動可能就收取如果另一個活動已擲回一個的錯誤,或在正常情況下如果父項的複合活動的控制項流程邏輯,決定取消工作。

當活動是可以取消時,執行階段就會將該活動的狀態設定為取消,並呼叫 Cancel 方法,活動上。 例如,Replicator 活動可以啟動的另一個用於每一段資料提供,一個子活動的數個反覆運算,並以平行方式執行這些活動的排程中。 它也具有 until­condition 屬性,會評估每一個子活動為關閉。 很可能,而且在 until­condition 的評估會可能,造成記憶體活動,以判斷應該完成。

為了複寫器,以關閉它必須先關閉所有的子活動。 因為每個這些活動已排程,而且可能執行 Replicator 活動會,檢查 ExecutionStatus 屬性的目前值和如果它 is 執行,會取消該活動 Runtime 要求。

使用補償

處理錯誤,工作流程中的允許開發人員立即的例外狀況處理。 使用交易也能夠範圍工作一起以確保一致性。 然而,在長的執行它有可能需要一致性,兩個單位的工作,但無法使用的交易工作流程。

例如,一旦工作流程開始時,它可能會更新可能新增客戶到 CRM 系統在 Line-of-Business 應用程式中的資料。 這項工作,可能會甚至是交易跨多個作業中的 CRM 與工作流程的狀態,提供一致的部分。 然後,等待從可能的使用者的進一步輸入會發生這種情況的天數之後,工作流程的帳戶處理系統,以更新客戶資訊。 很重要的帳戶處理系統] 和 [CRM 系統] 都有一致的資料,但並不可能對這些資源使用的不可部分完成的交易,在這類的大規模的時間範圍。 因此會成為問題,如何執行處理的更新第二個系統,確保一致性的第一個系統已認可的變更時所發生的例外狀況嗎?

fig04.gif

[圖 4 While 活動,重試邏輯

因為工作在兩個系統中的無法進行與交易一致,您需要是偵測錯誤發生於更新第二個系統,提供返回並復原工作,初始的系統中套用的機會或否則變更,以確保一致性機制。 雖然可以自動偵測這項變更,並將啟始這個處理序的動作時的初始系統的修正工作很明顯地會有由開發人員指定中。

WF 中的這個程序稱為補償] 和 [許多活動提供協助開發工作流程使用補償的。 如更多關於補償,以及如何使用補償相關的活動請 MSDN Magazine 在 2007 年 6 月的交易式工作流程上參閱 Dino Esposito 的前線資料行 (」 交易式工作流程").

重試活動

其中一個,問題與處理工作流程中的例外狀況是,當例外狀況發生時,即使您將它攔截,執行移上至下一個步驟處理序。 在許多的商務程序執行真的應該不會繼續之前,工作流程中所定義的商務邏輯執行成功。 開發人員經常會處理這藉由使用一些活動,以提供重試邏輯和定義的活動,以指示活動應繼續執行,只要發生的錯誤條件。 進一步,一個延遲活動通常用來防止重試邏輯立即發生。

若要這個重試] 模式中,您可以採用一個 Sequence 活動為一段的子活動。 進一步,特定的順序的工作單元通常包裝在另一個的順序或複合活動,來處理的例外狀況 acting 為容錯的處理範圍,與在錯誤處理常式檢視中所定義的所有錯誤處理常式。 然後 IfElse 活動通常是用來修改影響條件,時在工作流程的狀態活動。

在任何例外狀況發生的情況下,邏輯會設定屬性,或旗標的某種因此 While 活動可以關閉。 如果沒有發生例外狀況,則會在設定旗標,讓時再次,執行的活動和一個延遲活動用來進行下一個嘗試之前,先暫停。 圖 4 顯示的使用,時其中一個範例要重試活動的工作流程的活動。

如果要在許多案例中運作的這個特定的模式時, 想像一下 5 或 10 個不同作業需要重試的工作流程。 您快速地將要瞭解它是許多建置每個活動的重試邏輯的工作。 幸好,WF 都讓開發人員撰寫自訂包括自訂的複合活動的活動。 這表示我可以撰寫自己的重試活動,以封裝時發生例外狀況,再次執行子活動。 我要提供兩個金鑰的輸入給使用者這是重要: 在重試,] 與 [重試之前先設定讓例外狀況的泡泡工作,並處理最大的次數之間的延遲間隔。

在這個資料行的其餘部分,我將詳細說明中的重試活動邏輯。 如需建立自訂活動的背景資訊,請參閱第我先前的文章 (」 Windows Workflow: 建置自訂活動來擴充您的工作流程的在到達日期「,如需使用 activity­ExecutionContext 建立的活動,可以逐一查看子活動的詳細資訊,看到的這個資料行在 2007 年 6 月期 」 工作流程中的 ActivityExecutionContext").

正確管理子活動,很重要,要能夠監視活動,要知道當錯誤發生時。 因此,執行子活動時, 重試活動不只是註冊以便關閉,子活動,但也會登錄以便子活動會放入錯誤狀態時獲得通知時獲得通知。 [圖 5 會示範 BeginIteration 方法用來啟動子活動的每個反覆項目。 之前排程活動,關閉] 和 [Faulting 事件會有已註冊的處理常式。

[圖 5 執行子活動與註冊的錯誤

Activity child = EnabledActivities[0];
ActivityExecutionContext newContext = 
  executionContext.ExecutionContextManager.CreateExecutionContext(child);

newContext.Activity.Closed += 
  new 
EventHandler<ActivityExecutionStatusChangedEventArgs>(child_Closed);

newContext.Activity.Faulting += 
  new 
EventHandler<ActivityExecutionStatusChangedEventArgs>(Activity_Faulting);

newContext.ExecuteActivity(newContext.Activity);

通常如果在子活動錯誤,父活動會也會使處於錯誤狀態。 為了避免這的種情況,當兒童的活動錯誤重試活動的檢查,查看是否活動已重試最大次數。 如果已經到達重試計數,這個程式碼的空值就會出子活動,隱藏例外狀況,因此目前例外狀況:

void Activity_Faulting(object sender, 
  ActivityExecutionStatusChangedEventArgs e)
{
  e.Activity.Faulting -= Activity_Faulting;
  if(CurrentRetryAttempt < RetryCount) 
e.Activity.SetValue(
    ActivityExecutionContext.CurrentExceptionProperty, null);
}

當子活動關閉時,邏輯就會必須決定如何活動了為關閉狀態,,使用 ExecutionResult 屬性要執行這項操作。 因為在關閉的狀態,結束所有的活動,請在 ExecutionStatus 不提供判斷,實際的結果所需資訊,但在 ExecutionResult 指出是否活動錯誤、 成功,或已取消。 如果子活動成功,所沒有的重試然後重試活動只要關閉:

if (e.ExecutionResult == ActivityExecutionResult.Succeeded)
{
  this.SetValue(ActivityExecutionContext.CurrentExceptionProperty, null);
  thisContext.CloseActivity();
  return;
}

如果從關閉活動結果不是成功,尚未達到重試計數,然後必須再次,執行活動,但未重試之前間隔已過期。 在 [圖 6 ,而不是直接,開始另一個反覆項目的計時器訂閱建立使用活動上設定的間隔。

[圖 6] 建立計時器訂閱

if (CurrentRetryAttempt++ < RetryCount &&
    this.ExecutionStatus == ActivityExecutionStatus.Executing) {

  this.SetValue(ActivityExecutionContext.CurrentExceptionProperty, null);

  DateTime expires = DateTime.UtcNow.Add(RetryInterval);
  SubscriptionID = Guid.NewGuid();

  WorkflowQueuingService qSvc = 
    thisContext.GetService<WorkflowQueuingService>();
  WorkflowQueue q = qSvc.CreateWorkflowQueue(SubscriptionID, false);
  q.QueueItemAvailable += new EventHandler<QueueEventArgs>(TimerExpired);

  TimerEventSubscription subscription = new TimerEventSubscription(
    SubscriptionID, WorkflowInstanceId, expires);
  TimerEventSubscriptionCollection timers = 
    GetTimerSubscriptionCollection();
  timers.Add(subscription);

  return;
}

當計時器過期時,TimerExpired 會叫用方法,如下所示:

void TimerExpired(object sender, QueueEventArgs e)
{
  ActivityExecutionContext ctx = 
    sender as ActivityExecutionContext;
  CleanupSubscription(ctx);
  BeginIteration(ctx);
}

fig07.gif

[圖 7 : 在工作流程的重試活動

這將會開始子活動的下一個反覆項目。使用 TimerEventSubscription 類別,並加入工作流程的計時器集合上的計時器,活動就可以正確地參與永續性和繼續的任何持續性服務目前設定在執行階段的。重試間隔很長時整個工作流程可以採取的記憶體不足直到計時器過期為止。

此時已符合工作流程活動的索引鍵的行為。如果在子活動錯誤,重試活動將會無法錯誤。而它將會重試間隔,暫停,然後嘗試再次執行子活動。

最後一個步驟是處理大小寫的活動已達到重試計數,並在子活動已會繼續失敗。在這種情況下,Activity_Faulting 方法不會清除子活動,例外狀況,目標為,讓做為一般的活動) 錯誤。並會關閉子活動時, 重試活動也關閉。

之後所有重試,重試] 關閉時嘗試已經失敗,結果相同為,如果原始的工作已失敗順序。重試活動都可以有 FaultHandler 活動定義,而且這些錯誤處理常式只會執行之後已執行所有的重試]。使用這個模型會簡化的動作可能需要進行重試的工作流程開發時,還會維護相同的開發經驗工作流程的人員,考慮可以處理錯誤,如 [圖 7 ] 所示。

此外,錯誤處理常式會執行子活動時失敗,重試] 嘗試,讓工作流程開發人員可以選擇來處理,其中一個活動的錯誤。每個失敗,確保活動有機會清除上每個反覆項目的子活動上時,取得呼叫的 HandleFault 方法。

您問題或意見寄至mmnet30@Microsoft.com.

Matt Milner 會是的 Pluralsight,他會著重在連線的系統的技術,技術人員的成員。Matt 也是的獨立的顧問,專精於 Microsoft.NET 的技術,具有 Windows Workflow Foundation、 BizTalk Server、 在 ASP.NET 和 Windows Communication Foundation 上為焦點。Matt 會與他的妻子 Kristen 和兩個兒子住在明尼蘇達州。透過他的部落格,在連絡 Mattpluralsight.com/community/blogs/Matt.