Share via


Foundations

Fehlerbehandlung in Workflows

Matt Milner

Codedownload verfügbar in der MSDN-Codegalerie
Code online durchsuchen

Inhalt

Fehlerbehandlung in Workflows
Fehlerbehandlung im Hostprozess
Fehlerbehandlung in benutzerdefinierten Aktivitäten
Kompensierung
Retry-Aktivität

Windows Workflow Foundation (WF) bietet Tools zum Definieren leistungsfähiger Geschäftsprozesse sowie eine Laufzeitumgebung zum Ausführen und Verwalten dieser Prozesse. In jedem Geschäftsprozess gibt es Ausnahmen zum erwarteten Ausführungsfluss. Entwickler müssen daher in der Lage sein, eine stabile Anwendungslogik zu schreiben, um diese Ausnahmen zu bewältigen. Bei den meisten Technologien werden die Aspekte Fehlerbehandlung und Wiederherstellung übersehen. Im Artikel dieses Monats wird aufgezeigt, wie Sie Ausnahmen auf verschiedenen Ebenen des WF-Programmiermodells richtig behandeln. Außerdem wird erläutert, wie leistungsfähige Funktionen zur Ausnahmebehandlung in Workflows und Hosts integriert werden können.

Fehlerbehandlung in Workflows

Entwickler, die Geschäftsprozesse erstellen, müssen Ausnahmen in Bezug auf Geschäftsfälle behandeln können, damit der Prozess selbst stabil ist und nach Auftreten des Fehlers fortgeführt werden kann. Dies ist besonders bei Workflows wichtig, da sie oft in lang andauernden Prozessen eingesetzt werden. Ein unbehandelter Fehler erfordert daher meist einen Neustart des Prozesses. Das Standardverhalten für einen Workflow besteht darin, dass der Workflow beim Auftreten einer unbehandelten Ausnahme beendet wird. Folglich ist es für Workflowentwickler entscheidend, einen Bereich für die Arbeit zu bestimmen, Fehler zu behandeln und in den Workflow die Fähigkeit zu integrieren, bei einem Fehler die Arbeit zu wiederholen.

Die Fehlerbehandlung in Workflows hat vieles mit der Fehlerbehandlung in Code für Microsoft .NET Framework gemein, enthält aber auch einige neue Konzepte. Der erste Schritt bei der Fehlerbehandlung besteht darin, einen Ausführungsbereich festzulegen. In .NET-Code wird dies mit dem try-Schlüsselwort erreicht. Die meisten zusammengesetzten Aktivitäten in Workflows können dazu verwendet werden, einen Bereich für die Fehlerbehandlung zu erstellen. Für jede zusammengesetzte Aktivität werden in der Hauptansicht die untergeordneten Aktivitäten angezeigt. Außerdem stehen alternative Ansichten zur Verfügung. Das Kontextmenü einer Sequence-Aktivität in Abbildung 1 zeigt auf, wie auf verschiedene Ansichten zugegriffen werden kann. Außerdem sehen Sie das Ergebnis nach Auswahl der Option „Fehlerhandler anzeigen“.

Abbildung 1 Menü der alternativen Ansichten und Auswahl von „Fehlerhandler anzeigen“

Wenn Sie zur Ansicht „Fehlerhandler anzeigen“ wechseln, wird der Aktivitätenauflistung der Sequence-Aktivität eine FaultHandlers-Aktivität hinzugefügt. Innerhalb der FaultHandlers-Aktivität können einzelne FaultHandler-Aktivitäten hinzugefügt werden. Jede der FaultHandler-Aktivitäten verfügt über eine Eigenschaft zum Definieren des Fehlertyps und verhält sich wie ein catch-Ausdruck in .NET.

Die FaultHandler-Aktivität ist eine zusammengesetzte Aktivität, die Workflowentwicklern ermöglicht, untergeordnete Aktivitäten hinzuzufügen, um zu definieren, wie die Ausnahme behandelt werden soll. Diese Aktivitäten bieten möglicherweise Funktionen zur Fehlerprotokollierung, zur Kontaktaufnahme mit dem Administrator oder für andere Aktionen, die Sie normalerweise bei der Fehlerbehandlung in Ihrem Code durchführen.

Die FaultHandler-Aktivität besitzt zudem eine Fault-Eigenschaft, in der die erfasste Ausnahme enthalten ist. Untergeordnete Aktivitäten können diese Eigenschaft binden, um Zugriff auf die Ausnahme zu erlangen. Dies wird in Abbildung 2 gezeigt, in der die Ausnahmeeigenschaft einer benutzerdefinierten Protokollaktivität an die Fault-Eigenschaft der FaultHandler-Aktivität gebunden wird. Die Protokollaktivität kann nun die Ausnahmeinformationen an eine Protokoll-API, an das Windows-Ereignisprotokoll, an die Windows-Verwaltungsinstrumentation (Windows Management Instrumentation, WMI) oder an ein beliebiges anderes Ziel schreiben.

Abbildung 2 Binden an Fehler

Wie bei catch-Blöcken werden die FaultHandler-Aktivitäten aufgrund ihrer Fehlertypen ausgewertet. Beim Definieren des Workflows sollten die FaultHandler-Aktivitäten von links nach rechts und vom spezifischsten bis hin zum am wenigsten spezifischen Fehler hinzugefügt werden.

fig03.gif

Abbildung 3 Ausführung wird nach der zusammengesetzten Aktivität fortgesetzt

Wenn eine Ausnahme auftritt und in .NET-Code erfasst wird, wird nach Beendigung des catch-Blocks die Ausführung nach dem try-Bereich fortgesetzt. In einem Workflow wird die Ausführung also mit der nächsten Aktivität fortgesetzt, die auf die zusammengesetzte Aktivität zur Ausnahmebehandlung folgt (siehe Abbildung 3).

Es gibt zwei Hauptkonzepte, nach denen FaultHandler-Aktivtäten ausgewertet und ausgeführt werden. Wenn die Throw-Aktivität (wie in Abbildung 3) ausgeführt wird oder eine andere Aktivität eine Ausnahme auslöst, versetzt die Laufzeit die Aktivität in den faulted-Zustand und plant die Ausführung der HandleFault-Methode für die Aktivität. Einzelheiten dazu, wie Aktivitäten diese Methoden implementieren, folgen weiter unten. An dieser Stelle reicht die Feststellung, dass die Aktivität bereinigt werden kann.

Wenn die Aktivität bereinigt wurde und einen closed-Zustand erreicht, wird die übergeordnete Aktivität in den faulting-Zustand versetzt und erhält ebenfalls die Möglichkeit, untergeordnete Aktivitäten zu bereinigen und zu signalisieren, dass sie einen closed-Zustand annehmen kann. An dieser Stelle, wenn die zusammengesetzte Aktivität also signalisiert, dass ein closed-Zustand angenommen werden kann, überprüft die Laufzeit den Status. Wenn es sich um „Faulting“ handelt, wird die FaultHandlers-Auflistung überprüft. Wenn eine FaultHandler-Aktivität mit einem Fault-Typ gefunden wird, der der aktuellen Ausnahme entspricht, wird der Einsatz von FaultHandler geplant und die Auswertung angehalten. Nach dem Schließen von FaultHandler kann die Ausführung dann mit der nächsten Aktivität fortgesetzt werden.

Beim Auftreten einer Ausnahme versucht die Laufzeit, Fehlerbehandlungsaktivitäten in der direkt übergeordneten zusammengesetzten Aktivität zu finden. Wenn keine entsprechenden Handler gefunden werden, wird die zusammengesetzte Aktivität als fehlerhaft eingestuft, und die Ausnahme wird an die nächste zusammengesetzte Aktivität in der Struktur geleitet. Dies ähnelt dem Vorgang in .NET, bei dem unbehandelte Ausnahmen innerhalb des Stapels nach oben an die Aufrufmethoden geleitet werden. Wenn die Ausnahme bis hin zur Stammaktivität des Workflows geleitet wird, ohne dass ein Handler gefunden wird, wird der Workflow beendet.

Beachten Sie, dass der Workflow selbst eine zusammengesetzte Aktivität ist und daher für ihn möglicherweise eine Fehlerbehandlungslogik zur Behandlung von Ausnahmen definiert ist, die die höchste Ebene erreicht haben. Dies ist die letzte Möglichkeit für einen Workflowentwickler, Ausnahmen zu erfassen und innerhalb des Geschäftsprozesses zu behandeln.

Fehlerbehandlung im Hostprozess

Stabile Workflows sind zwar wichtig, aber genauso wichtig ist es für eine stabile Anwendung, dass der Host Ausnahmen behandeln kann. Glücklicherweise ist die Workflowlaufzeit standardmäßig in der Lage, Ausnahmen zu behandeln und den Hostprozess vor den meisten Ausnahmen zu schützen.

Wenn eine Ausnahme in einem Workflow auftritt, durch die Hierarchie geleitet und nicht erfasst wird, wird der Workflow beendet und für die Laufzeit ein Ereignis ausgelöst. Der Laufzeithost kann einen Handler registrieren, der beim Auftreten einer Ausnahme benachrichtigt wird, aber die Ausnahme hat nicht zur Folge, dass der Host abstürzt. Um über eine Beendigung benachrichtigt zu werden, kann im Hostprozess der folgende Code verwendet werden, der Informationen über die Ausnahme aus den Ereignisargumenten abruft:

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

Zusätzlich zur Behandlung beendeter Workflows kann der Hostprozess über Ausnahmen benachrichtigt werden, die in Laufzeitdiensten auftreten. Wenn SqlWorkflowPersistenceService beispielsweise in die Laufzeit geladen wird, fragt die Eigenschaft die Datenbank ab und versucht möglicherweise, von Zeit zu Zeit Workflows zu laden, wenn diese weitere Arbeit auszuführen haben. Beim Ladeversuch eines Workflows löst der Persistenzdienst u. U. eine Ausnahme aus, beispielsweise beim Deserialisieren des Workflows. Wenn dies geschieht, ist es wiederum wichtig, dass der Hostprozess nicht fehlschlägt. Daher werden Ausnahmen dieser Art nicht ein zweites Mal von den betreffenden Diensten ausgelöst. Stattdessen wird bei der Workflowlaufzeit ein Ereignis ausgelöst. Die Laufzeit wiederum löst ein ServicesExceptionNotHandled-Ereignis aus, das in Code behandelt werden kann, wie hier gezeigt:

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

Im Allgemeinen müssen Entwickler von Laufzeitdiensten entscheiden, ob eine erfasste Ausnahme kritisch ist oder nicht. Wenn SqlWorkflowPersistenceService keinen einzigen Workflow laden kann, bedeutet dies nicht, dass der Dienst nicht funktionieren kann. Daher ist es in diesem Fall logisch, einfach ein Ereignis auszulösen, damit der Hostprozess bestimmen kann, ob weitere Aktionen erforderlich sind. Wenn der Persistenzdienst aber keine Verbindung zur SQL Server-Datenbank herstellen kann, kann er nicht funktionieren. Statt ein Ereignis auszulösen, ist es in diesem Fall sinnvoller, dass der Dienst eine Ausnahme auslöst und der Host angehalten wird, sodass das Problem gelöst werden kann.

Der empfohlene Ansatz beim Entwickeln benutzerdefinierter Laufzeitdienste besteht darin, diese Dienste von der WorkflowRuntimeService-Basisklasse abzuleiten. Diese Basisklasse bietet sowohl Zugriff auf die Laufzeit als auch eine geschützte Methode, um ein ServicesExceptionNotHandled-Ereignis auszulösen. Wenn bei Ausführung eines Laufzeitdiensts eine Ausnahme auftritt, sollte der Dienst nur dann eine Ausnahme auslösen, wenn der Fehler wirklich nicht behebbar ist. Wenn sich der Fehler auf eine einzelne Workflowinstanz bezieht, nicht auf die allgemeine Dienstausführung, sollte stattdessen ein Ereignis ausgelöst werden.

Fehlerbehandlung in benutzerdefinierten Aktivitäten

Für Ersteller von Aktivitäten hat die Ausnahmebehandlung eine etwas andere Bedeutung. Bei der Ausnahmebehandlung in Aktivitäten gibt es zwei Ziele: das Behandeln auftretender Ausnahmen, um sie möglichst daran zu hindern, weitergeleitet zu werden und den Workflow zu unterbrechen, und eine ordentliche Bereinigung in Fällen, bei denen ein Ausnahmefehler aus der Aktivität weitergeleitet wird.

Da eine Aktivität einfach eine Klasse ist, ist das Behandeln von Ausnahmen innerhalb der Aktivität nicht anders als in anderen Klassen. Es werden try/catch-Blöcke zum Aufrufen anderer Komponenten verwendet, die möglicherweise Fehler auslösen. Wenn jedoch eine Ausnahme in einer Aktivität erfasst wird, müssen Sie entscheiden, ob die Ausnahme erneut ausgelöst werden soll. Wenn sich die Art der Ausnahme nicht auf das Ergebnis der Aktivität auswirkt oder wenn die Aktivität auf kontrolliertere Weise anzeigen kann, dass sie nicht erfolgreich war, ist dies die bevorzugte Methode für das Feedback. Wenn die Ausnahme aber bedeutet, dass die Aktivität fehlgeschlagen ist und die Verarbeitung nicht abgeschlossen werden konnte bzw. keine Fehleranzeige möglich war, sollte eine Ausnahme ausgelöst werden, sodass der Workflowentwickler den Geschäftsprozess so entwerfen kann, dass die Ausnahme behandelt wird.

Der andere Aspekt bei der Ausnahmebehandlung besteht darin, die Aktivitätsressourcen zu bereinigen. Im Unterschied zu einem Workflow, bei dem sich die Fehlerbehandlung auf den Geschäftsprozess, die Protokollierung und die Benachrichtigung konzentriert, liegt der Schwerpunkt bei der Fehlerbehandlung in Aktivitäten in erster Linie auf dem Bereinigen der Ressourcen, die bei Ausführung der Aktivität verwendet wurden.

Die Art der Fehlerbehandlung hängt auch davon ab, ob Sie eine Blattaktivität oder eine zusammengesetzte Aktivität schreiben. In einer Blattaktivität wird die HandleFault-Methode aufgerufen, wenn ein Ausnahmefehler von der Laufzeit erfasst wird, damit die Aktivität die möglicherweise verwendeten Ressourcen freigeben und eine begonnene Ausführung bereinigen kann. Wenn die Aktivität beispielsweise zur Ausführung eine Datenbank verwendet, sollte in der HandleFault-Methode sichergestellt werden, dass die Verbindung geschlossen wird, falls dies noch nicht geschehen ist, und dass alle weiteren gerade verwendeten Ressourcen entfernt werden. Wenn die Aktivität eine beliebige asynchrone Arbeit aufgenommen hat, sollten die Arbeit an dieser Stelle abgebrochen und die entsprechenden Verarbeitungsressourcen freigesetzt werden.

Wenn die HandleFault-Methode bei einer zusammengesetzten Aktivität auftritt, liegt dies möglicherweise an einem Logikfehler in der Aktivität selbst oder daran, dass eine untergeordnete Aktivität einen Fehler aufweist. In beiden Fällen liegt der Zweck beim Aufrufen der HandleFault-Methode für eine zusammengesetzte Aktivität darin, der Aktivität eine Bereinigung ihrer untergeordneten Aktivitäten zu ermöglichen. Bei einer solchen Bereinigung muss sichergestellt werden, dass die zusammengesetzte Aktivität keine weiteren Aktivitäten ausführen lässt und dass alle gerade ausgeführten Aktivitäten abgebrochen werden. Glücklicherweise ruft die Standardimplementierung der HandleFault-Methode, wie in der CompositeActivity-Basisklasse definiert, die Cancel-Methode für die zusammengesetzte Aktivität auf.

Abbruch ist ein weiterer Mechanismus, mit dem Aktivitäten, die Arbeit asynchron begonnen haben und auf die Fertigstellung der Arbeit warten, benachrichtigt werden können, dass die angefangene Arbeit abgebrochen werden sollte und die Ressourcen bereinigt werden sollten, damit sie geschlossenen werden können. Eine Aktivität darf abgebrochen werden, wenn eine andere Aktivität einen Fehler ausgelöst hat oder, unter normalen Umständen, wenn die Ablaufsteuerungslogik der übergeordneten zusammengesetzten Aktivität entscheidet, die Arbeit abzubrechen.

Wenn eine Aktivität abgebrochen wird, setzt die Laufzeit den Status dieser Aktivität auf „Canceling“ und ruft die Cancel-Methode für die Aktivität auf. Beispielsweise kann die Replicator-Aktivität mehrere Iterationen einer untergeordneten Aktivität starten, und zwar eine für jedes bereitgestellte Datenelement, und eine parallele Ausführung dieser Aktivitäten planen. Sie enthält dazu eine UntilCondition-Eigenschaft, die beim Schließen der einzelnen untergeordneten Aktivitäten ausgewertet wird. Diese Auswertung von UntilCondition führt wahrscheinlich dazu, dass die Aktivität feststellt, dass sie geschlossen werden sollte.

Vor dem Schließen der Replicator-Aktivität müssen zunächst alle untergeordneten Aktivitäten geschlossen werden. Da jede dieser Aktivitäten bereits geplant wurde und möglicherweise ausgeführt wird, prüft die Replicator-Aktivität den aktuellen Wert der ExecutionStatus-Eigenschaft. Wenn der Wert „Executing“ lautet, fordert die Replicator-Aktivität bei der Laufzeit an, diese Aktivität abzubrechen.

Kompensierung

Die Fehlerbehandlung in Workflows ermöglicht Entwicklern, unmittelbare Ausnahmebedingungen zu behandeln. Die Verwendung von Transaktionen bietet zudem die Möglichkeit, gemeinsame Bereiche für die Arbeit festzulegen, um Konsistenz zu gewährleisten. In lang andauernden Workflows ist es jedoch möglich, dass zwei Arbeitseinheiten konsistent sein müssen, aber keine Transaktion verwenden können.

Zum Beispiel aktualisiert ein gestarteter Workflow möglicherweise die Daten in einer Geschäftsanwendung, indem dem CRM-System ein neuer Kunde hinzugefügt wird. Diese Arbeit ist vielleicht sogar Teil einer Transaktion, um Konsistenz über mehrere Vorgänge im CRM und im Workflowzustand zu bieten. Wenn der Workflow dann möglicherweise tagelang auf eine Benutzereingabe gewartet hat, aktualisiert er ein Buchhaltungssystem mit den Kundeninformationen. Es ist wichtig, dass sowohl das Buchhaltungssystem als auch das CRM-System konsistente Daten aufweisen, aber es ist nicht möglich, dass für diese Ressourcen über einen solch langen Zeitraum eine einzelne Transaktion verwendet wird. Die Frage ist daher jetzt, wie Ausnahmen behandelt werden sollen, die beim Aktualisieren des zweiten Systems auftreten, um Konsistenz mit den bereits im ersten System vorgenommenen Änderungen zu gewährleisten?

fig04.gif

Abbildung 4 While-Aktivität als Wiederholungslogik

Da für die Arbeit in den beiden Systemen Konsistenz nicht mit einer Transaktion erzielt werden kann, benötigen Sie beim Aktualisieren des zweiten Systems einen Mechanismus zur Fehlererkennung und eine Möglichkeit, die Arbeit im Anfangssystem rückgängig zu machen bzw. andere Änderungen vorzunehmen, die Konsistenz gewährleisten. Das Erkennen dieser Änderung und das Initiieren des Prozesses können automatisch erfolgen, aber die Änderungen am Anfangssystem müssen natürlich durch den Entwickler festgelegt werden.

In WF wird dieser Prozess als „Kompensierung“ bezeichnet. Es werden verschiedene Aktivitäten zum Entwickeln von Workflows bereitgestellt, die Kompensierung verwenden. Weitere Informationen zur Kompensierung und zur Verwendung von Kompensierungsaktivitäten finden Sie im Cutting Edge-Artikel Transaktionsworkflows von Dino Esposito im MSDN Magazin vom Juni 2007.

Retry-Aktivität

Eines der Probleme bei der Ausnahmebehandlung in Workflows ist, dass selbst bei einer erfassten Ausnahme die Ausführung zum nächsten Prozessschritt fortschreitet. In vielen Geschäftsprozessen sollte die Ausführung nicht fortgesetzt werden, bis die im Workflow definierte Geschäftslogik erfolgreich ausgeführt wird. Entwickler behandeln dies oft mithilfe einer While-Aktivität, um eine Wiederholungslogik bereitzustellen und die Bedingung der Aktivität zu definieren, die anzeigt, dass die Aktivität weiter ausgeführt werden soll, solange ein Fehler aufgetreten ist. Außerdem wird oft eine Delay-Aktivität eingesetzt, um zu verhindern, dass die Wiederholungslogik sofort angewendet wird.

Zur Aktivierung dieses Wiederholungsmodells können Sie eine Sequence-Aktivität als untergeordnete Aktivität der While-Aktivität verwenden. Außerdem wird eine spezifische Arbeitseinheit in der Sequenz zur Ausnahmebehandlung oft von einer anderen Sequenz oder zusammengesetzten Aktivität umschlossen, die für alle in der FaultHandlers-Ansicht definierten Fehlerhandler als Bereich für die Fehlerbehandlung fungieren. Anschließend wird oft eine IfElse-Aktivität verwendet, um den Workflowzustand zu ändern und die Bedingung der While-Aktivität zu beeinflussen.

Wenn keine Ausnahme auftritt, setzt die Logik eine Eigenschaft oder eine Art Kennzeichen, sodass die While-Aktivität geschlossen werden kann. Beim Auftreten einer Ausnahme wird das Kennzeichen so gesetzt, dass die While-Aktivität wieder ausgeführt wird, und eine Delay-Aktivität wird zum Pausieren verwendet, bis der nächste Versuch erfolgt. Abbildung 4 zeigt ein Beispiel für das Verwenden der While-Aktivität, um Aktivitäten in einem Workflow zu wiederholen.

Dieses Muster funktioniert zwar in vielen Szenarios, aber stellen Sie sich einen Workflow mit 5 oder 10 Vorgängen vor, die wiederholt werden müssen. Sie werden schnell erkennen, dass es viel Arbeit ist, die Wiederholungslogik für jede Aktivität zu erstellen. Glücklicherweise ermöglicht WF Entwicklern das Schreiben benutzerdefinierter Aktivitäten, einschließlich benutzerdefinierter zusammengesetzter Aktivitäten. Sie können also Ihre eigene Retry-Aktivität schreiben, um die Ausführung der untergeordneten Aktivitäten zu kapseln, wenn eine Ausnahme auftritt. Um hieraus Nutzen zu ziehen, werden an dieser Stelle zwei wichtige Benutzereingaben bereitgestellt: ein Verzögerungsintervall zwischen den Wiederholungen und die maximale Anzahl von Wiederholungsversuchen für die Arbeit, bevor die Ausnahme weitergeleitet und behandelt wird.

Im verbleibenden Teil dieses Artikels wird die Logik in der Retry-Aktivität erläutert. Hintergrundinformationen zum Erstellen benutzerdefinierter Aktivitäten finden Sie in meinem früheren Artikel Windows Workflow: Erstellen Sie benutzerdefinierte Aktivitäten, um Ihre Workflows zu erweitern. Informationen zum Verwenden von ActivityExecutionContext für das Erstellen von Aktivitäten, die über einer untergeordneten Aktivität wiederholt werden, finden Sie in der Ausgabe dieser Rubrik vom Juni 2007 (ActivityExecutionContext in Workflows).

Um die untergeordnete Aktivität richtig zu verwalten, muss die Aktivität unbedingt überwacht werden, damit auftretende Fehler erkannt werden. Daher registriert sich die Wiederholungsaktivität beim Ausführen einer untergeordneten Aktivität nicht nur für eine Benachrichtigung, wenn die untergeordnete Aktivität geschlossen wird, sondern auch für eine Benachrichtigung, wenn die untergeordnete Aktivität in den Faulting-Zustand übergeht. Abbildung 5 zeigt die BeginIteration-Methode, die zum Starten aller Iterationen der untergeordneten Aktivität verwendet wird. Vor dem Planen der Aktivität müssen für die Closed- und Faulting-Ereignisse Handler registriert werden.

Abbildung 5 Ausführen untergeordneter Aktivitäten und Registrieren für Fehler

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

Wenn eine untergeordnete Aktivität einen Fehler aufweist, wird normalerweise die übergeordnete Aktivität ebenfalls in den faulting-Zustand versetzt. Um diese Situation zu vermeiden, überprüft die Retry-Aktivität bei einem Fehler in der untergeordneten Aktivität, ob für die Aktivität bereits die maximale Anzahl an Wiederholungen durchgeführt wurde. Wenn die entsprechende Wiederholungszahl noch nicht erreicht ist, macht der Code die aktuelle Ausnahme in der untergeordneten Aktivität ungültig und unterdrückt somit die Ausnahme:

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

Wenn die untergeordnete Aktivität schließt, muss die Logik bestimmen, wie die Aktivität den closed-Zustand erreicht hat. Dazu wird die ExecutionResult-Eigenschaft verwendet. Da alle Aktivitäten im Closed-Zustand enden, stellt ExecutionStatus nicht die erforderlichen Informationen bereit, um das eigentliche Ergebnis zu bestimmen, zeigt aber an, ob die Aktivität fehlerhaft oder erfolgreich war bzw. abgebrochen wurde. Wenn die untergeordnete Aktivität erfolgreich war, ist keine Wiederholung erforderlich, und die Retry-Aktivität wird einfach geschlossen:

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

Wenn das Ergebnis der Schließaktivität nicht erfolgreich ist und der Wiederholungszahl noch nicht erreicht ist, muss die Aktivität erneut ausgeführt werden, aber erst dann, wenn das Wiederholungsintervall abgelaufen ist. In Abbildung 6 wird statt einer direkten erneuten Iteration mithilfe des für die Aktivität konfigurierten Intervalls ein Timerabonnement erstellt.

Abbildung 6 Erstellen eines Timerabonnements

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

Wenn der Timer abläuft, wird die TimerExpired-Methode aufgerufen, wie im Folgenden gezeigt:

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

fig07.gif

Abbildung 7 Wiederholung in einem Workflow

So beginnt die nächste Iteration der untergeordneten Aktivität. Mithilfe der TimerEventSubscription-Klasse und durch Hinzufügen des Timers zur Timerauflistung des Workflows kann die Aktivität korrekt an Persistenzdiensten teilnehmen und wird mit dem aktuell in der Laufzeit konfigurierten Persistenzdienst fortgesetzt. Wenn das Wiederholungsintervall lang ist, wird der gesamte Workflow möglicherweise aus dem Speicher genommen, bis der Timer abläuft.

Das Schlüsselverhalten der Workflowaktivität ist jetzt erreicht. Wenn eine untergeordnete Aktivität einen Fehler aufweist, ergibt sich daraus kein Fehler bei der Wiederholungsaktivität. Stattdessen wird die Wiederholungsaktivität während des Wiederholungsintervalls angehalten und versucht dann, die untergeordnete Aktivität erneut auszuführen.

Im letzten Schritt wird der Fall behandelt, bei dem die Aktivität die Wiederholungszahl erreicht hat und die untergeordnete Aktivität nach wie vor fehlerhaft ist. In diesem Fall löscht die Activity_Faulting-Methode die Ausnahme in der untergeordneten Aktivität nicht, da das Ziel darin besteht, den Fehler in der Aktivität ganz normal ausführen zu lassen. Wenn die untergeordnete Aktivität geschlossen wird, wird die Retry-Aktivität ebenfalls geschlossen.

Wenn alle Wiederholungsversuche fehlgeschlagen sind und die Retry-Aktivität geschlossen wird, ist das Ergebnis das gleiche, als wenn die Originalarbeit in einer Sequenz fehlgeschlagen wäre. Für die Retry-Aktivität können FaultHandler-Aktivitäten definiert werden, die erst ausgeführt werden, wenn alle Wiederholungen durchgeführt wurden. Durch Einsatz dieses Modells lässt sich die Entwicklung von Workflows mit Aktionen vereinfachen, die möglicherweise erneut ausgeführt werden müssen, aber gleichzeitig wird Workflowentwicklern eine einheitliche Entwicklungsfunktionalität für die Fehlerbehandlung geboten (siehe Abbildung 7).

Außerdem werden die Fehlerhandler für die untergeordnete Aktivität ausgeführt, wenn alle Wiederholungsversuche fehlgeschlagen sind. So haben Workflowentwickler die Wahl, die Fehler für beide Aktivitäten zu behandeln. Die HandleFault-Methode wird für alle Fehler der untergeordneten Aktivität aufgerufen. Dadurch wird sichergestellt, dass die Aktivität bei jeder Iteration die Möglichkeit erhält, eine Bereinigung vorzunehmen.

Senden Sie Fragen und Kommentare in englischer Sprache an mmnet30@microsoft.com.

Matt Milner ist technischer Mitarbeiter bei Pluralsight. Er konzentriert sich auf Technologien für verbundene Systeme. Außerdem ist Matt Milner unabhängiger Berater im Bereich Microsoft .NET-Technologien mit dem Schwerpunkt Windows Workflow Foundation, BizTalk Server, ASP.NET und Windows Communication Foundation. Matt Milner lebt mit seiner Frau Kristen und seinen zwei Söhnen in Minnesota. Sie erreichen ihn über seinen Blog unter pluralsight.com/community/blogs/matt.