MSDN Magazin > Home > Ausgaben > 2007 > June >  Foundations: ActivityExecutionContext in Workfl...
Foundations
ActivityExecutionContext in Workflows
Matt Milner

Codedownload verfügbar unter: Foundations2007_06.exe (179 KB)
Browse the Code Online
In diesem Monat werde ich auf ActivityExecutionContext (AEC) in Windows® Workflow Foundation eingehen. Diese spezielle Klasse ist eine wichtige Komponente der Ausführung, Persistenz und Kompensierung von Workflows und ihrer Aktivitäten, aber dennoch wird ihnen in der Dokumentation und in den Beispielen nur wenig Aufmerksamkeit gewidmet. Viele der Probleme, mit denen Entwickler beim Erstellen von Workflows und Aktivitäten häufig konfrontiert werden, haben damit zu tun, wie ActivityExecutionContext arbeitet. Wenn Sie die While-Aktivität verwenden, einen Statuscomputerworkflow erstellen oder benutzerdefinierte Aktivitäten erstellen, ist es wichtig zu wissen, wie Sie mit dem AEC arbeiten können. Aus diesem Grund werde ich in diesem Artikel auf ActivityExecutionContext eingehen, Ihnen zeigen, wie Sie es in Ihren Workflows nutzen können, und erklären, wie Sie es beim Schreiben benutzerdefinierter Aktivitäten verwenden können.

Die Notwendigkeit von ActivityExecutionContext
Es gibt mehrere Funktionen von Windows Workflow Foundation, die AEC erfordern und das Design von AEC bestimmen. Bei diesen Funktionen handelt es sich um die Statusverwaltung (oder Persistenz), die erneute Ausführung von Aktivitäten und die Kompensierung. Die Statusverwaltungsdienste von Windows Workflow Foundation stellen die Möglichkeit bereit, den ganzen Workflowstatus zu serialisieren und diese Informationen in einem dauerhaften Speicher abzulegen, damit ein Workflow aus dem Speicher entfernt und später wieder fortgesetzt werden kann. Um dies zu ermöglichen, muss es einen Container für die auf das Workingset bezogenen Statusinformationen geben, die die Aktivitätsstruktur, Kommunikationswarteschlangen und internen Datenstrukturen für die Planung und die Ausführung umfassen. Die Aktivitätsstruktur wird häufig als Anwendungszustand bezeichnet, da dies der Zustand ist, der vom Anwendungsentwickler erstellt wurde und in den Aktivitäten enthalten ist, die zum Definieren des Workflows verwendet werden. Der restliche Zustand wird als Laufzeitstatus bezeichnet, da er in erster Linie von der Windows Workflow Foundation-Laufzeit verwendet wird, um die Workflow-Ausführung zu verwalten.
Die beiden anderen Funktionen, die AEC erfordern, sind eng damit verwandt. Zu einem gemeinsamen Entwurfsmuster in Workflows gehören sich wiederholende Schritte. Denken Sie an die While-Aktivität, die ihre untergeordnete Aktivität so lange wiederholt, bis eine bestimmte Bedingung erfüllt wird. Wenn Sie die zugrunde liegende Implementierung dieser Ausführung betrachten, gibt es zwei Hauptmöglichkeiten, dies durchzuführen. Bei der ersten Möglichkeit werden die Aktivitätsinstanzen in jeder Iteration erneut verwendet und vor jeder Ausführung auf ihren ursprünglichen Status zurückgesetzt. Dies funktioniert zwar evtl. bei Aktivitäten wie der While-Aktivität, doch wie steht es mit der Replicator-Aktivität, die ihre untergeordnete Aktivität parallel ausführen kann? In diesem Fall müssen Sie evtl. drei oder vier verschiedene Instanzen der Aktivitäten gleichzeitig ausführen lassen. Das Rücksetzungsverfahren wird in dieser Situation eindeutig nicht funktionieren.
Die zweite Möglichkeit, um Aktivitäten mehrmals auszuführen, besteht darin, die Originalaktivitäten zu klonen und anschließend in jeder Iteration die geklonten Aktivitäten ausführen. In diesem Modell beginnt jede Iteration der Aktivitäten mit derselben Vorlage (dem ursprünglichen Status der Aktivitäten) und kann jede geklonte Instanz separat verwaltet werden. Einer der Vorteile dieses Modells liegt darin, dass zur Laufzeit der Status jedes Klons einzeln verwaltet werden kann, wodurch die anderen Klone nicht gestört werden. Jeder dieser Klone erfordert, dass sein eigener AEC den Status dieser Instanz der Aktivitäten verwaltet.
Kompensierung ermöglicht, dass eine Aktivität noch etwas Arbeit leistet, nachdem sie ihren Hauptausführungspfad bereits abgeschlossen hat. Dadurch kann eine Aktivität mit Fällen umgehen, in denen nach dem Abschluss der Aktivität Fehler im Prozess auftreten, sodass Aktivitäten zu einem Status zurückkehren können, der mit dem ursprünglichen Status konsistent ist. Dies könnte zum Beispiel bedeuten, dass Systeme oder Daten aktualisiert werden, die bereits geändert wurden. Im Grunde erfordert Kompensierung, dass ein Satz von Aktionen, die von einer Aktivität durchgeführt wurden, berücksichtigt werden kann, nachdem die Aktivität ihre Arbeit bereits abgeschlossen hat. Um dies zu erlauben, muss es möglich sein, den Status dieser Arbeit zu speichern und wiederherzustellen. Um die von einer bestimmten Instanz vorgenommenen Änderungen zu kompensieren, aber nicht die von einer anderen Instanz vorgenommenen Änderungen, muss der Status dieser Aktivität in jeder Iteration verwaltet werden können. Daher hilft die Verwendung des AEC für jede Iteration eines Satzes von Aktivitäten, um die Kompensierung zu ermöglichen, indem einer Aktivität gestattet wird, Zugriff auf den richtigen Status zu erhalten, den sie bei der Ausführung verwendet hat.
Der AEC fungiert als Behälter für den Laufzeitstatus und den Anwendungszustand, und jeder Workflow besitzt einen einzigen Standard-AEC für die Stammaktivität (den Workflow selbst) und kann zusätzlich untergeordnete AECs besitzen, falls von Aktivitäten AECs erstellt werden. Wenn beispielsweise ein Workflow mit einer While-Aktivität vorliegt und diese While-Aktivität, wie in Abbildung 1 gezeigt, eine einzige Code-Aktivität besitzt, dann erstellt jede Iteration der While-Aktivität einen untergeordneten AEC.
Abbildung 1 ActivityExecutionContexts mit einer einfachen While-Aktivität (Klicken Sie zum Vergrößern auf das Bild)

Workflow-Entwicklung
Ein häufiges Problem von Entwicklern besteht darin zu versuchen, Code in einen Ereignishandler zu schreiben, wenn die Aktivität in einer Wiederholungsaktivität auftritt. Nehmen Sie beispielsweise einmal an, innerhalb einer While-Aktivität befänden sich mehrere Aktivitäten und Sie würden, wie in Abbildung 2 gezeigt, von einem Ereignishandler einer anderen Aktivität auf eine dieser Aktivitäten verweisen wollen. Zu Demonstrationszwecken werde ich die Code-Aktivität und eine benutzerdefinierte Aktivität namens „ReadLine“ verwenden, die eine Eingabe von der Konsole einliest. Die Aktivität „ReadLine“ hat eine einzige Eigenschaft, die den Text verfügbar macht, der an der Konsole eingegeben wurde. In diesem Beispiel will ich auf die Text-Eigenschaft der Aktivität „ReadLine“ im ExecuteCode-Ereignishandler der Code-Aktivität zugreifen.
Abbildung 2 Ereignishandler 
Ihr erster Gedanke könnte sein, dass ich Code schreiben werde, um direkt auf die Aktivität „ReadLine“ zuzugreifen:
Console.WriteLine(“Text from ReadLine2: {0}”, readLine2.Text);
Doch wenn ich dies tue und den Workflow ausführe, erhalte ich eine Ausgabe wie in Abbildung 3. Das Problem liegt darin, dass ich auf die Aktivität „ReadLine“ von der Vorlage und nicht von der Instanz aus verweise, die für diese Iteration der While-Aktivität geklont wurde. Ein häufiges Verfahren für die Problembewältigung besteht darin, einen Verweis auf die aktuelle geklonte Instanz der Aktivitäten zu erwerben, um auf die richtige geklonte Instanz des Ziels (in diesem Fall die Aktivität „ReadLine“) zu verweisen.
Abbildung 3 Fehlende Ausgabe bei einem direkten Verweis (Klicken Sie zum Vergrößern auf das Bild)
Um einen Verweis zur richtigen Struktur geklonter Aktivitäten in einem Ereignishandler zu erwerben, können Sie den Absender des Ereignisses verwenden, bei dem es sich um die Aktivität handelt, in der das Ereignis definiert ist. In meinem Beispiel ist dies die Code-Aktivität. Von dort aus kann ich in der Aktivitätsstruktur navigieren, um die Aktivität zu finden, für die ich mich interessiere. Dieses Verfahren wird im folgenden Code gezeigt (ich kann diesen Code anstelle des vorherigen Codes verwenden, um das gewünschte Ergebnis zu erhalten):
CodeActivity thisActivityInstance = sender as 
  CodeActivity;
CompositeActivity parent = 
  thisActivityInstance.Parent as CompositeActivity;

MSDNMag.WFActivities.ReadLine target = 
  (MSDNMag.WFActivities.ReadLine) 
  parent.GetActivityByName(“readLine2”);
Console.WriteLine(
  “Text from ReadLine in this AEC: {0}”, target.Text);
In diesem Beispiel erhalte ich einen Verweis zur Instanz der Code-Aktivität, mit der ich arbeite, und navigiere über die übergeordnete Aktivität zur richtigen untergeordneten Aktivität. Danach kann ich auf diese spezielle Instanz zugreifen, um aus dieser Instanz den Wert der Text-Eigenschaft abzurufen.
Eine weitere Möglichkeit besteht darin, zwischen Aktivitäten freigegebene Daten zu verwenden. Dies würde einer Aktivität, die in einem untergeordneten AEC ausgeführt wird, ermöglichen, auf Daten des Standard-AECs (des AECs des Workflows selbst) zuzugreifen. In meinem Beispiel würde ich eine Zeichenfolgenvariable in der Workflowklasse definieren und danach die Text-Eigenschaft der Aktivität „ReadLine“ an diese Eigenschaft binden. Dies würde sicherstellen, dass diese Eigenschaft bei der Ausführung von ReadLine auf den richtigen Wert eingestellt wird. Anschließend könnte die Code-Aktivität auf die Daten im Workflow verweisen, der auf dem neuesten Stand wäre. Durch die Verwendung dieses Modells vermeide ich das Problem der geklonten Aktivitäten und des Verweises auf die Vorlage, indem ich auf die einzelne Instanz des Workflow verweise.
Dieses Verfahren eignet sich gut für die While-Aktivität, bei der immer nur eine einzige Iteration aktiv ist, doch die Sache wird komplizierter, wenn Sie beginnen, Aktivitäten parallel unter Verwendung der Replicator-, ConditionedActivityGroup- oder einer benutzerdefinierten Aktivität auszuführen. In diesem Fall müsste der freigegebene Status in einer Art verschlüsselter Sammlung verwaltet werden, was dann auch die Verwaltung eines Schlüssels erfordern würde.
Die sauberste und meiner Meinung nach beste Lösung besteht darin, eine benutzerdefinierte Aktivität und Aktivitätsbindung zu verwenden. Für mein einfaches Beispiel könnte ich die benutzerdefinierte Aktivität „WriteLine“ verwenden und ihre Text-Eigenschaft an die Text-Eigenschaft der Aktivität „ReadLine“ binden. Wenn Sie Aktivitätsbindung innerhalb eines AECs verwenden, wird die Zielaktivität innerhalb desselben AECs korrekt in die geklonte Instanz aufgelöst. Dies stellt ein einfacheres Modell zum Verbinden Ihrer Aktivitäten und zum Erzielen der Ergebnisse bereit, die Sie erwarten.
Jedes dieser drei Modelle für die Arbeit mit AECs wird in dem Beispielcode vorgeführt, der für diesen Artikel auf der MSDN®-Magazin-Website verfügbar ist. Der Workflow AECSamples enthält drei While-Aktivitäten, von denen jede eine der beschriebenen Methoden demonstriert, um die von Ihnen erwarteten Resultate zu erhalten.
Statuscomputerworkflows verursachen häufig ähnliche Probleme, wie sie im vorherigen Beispiel beschrieben wurden, die oft auch ähnliche, wenn auch nicht immer ersichtliche, Gründe haben. Statuscomputerworkflows dienen dem Zweck, eine Prozessausführung basierend auf einem Satz sich ändernder Zustände zu modellieren. Die Stärke dieses Modells liegt darin, dass der Prozess oder die Sache, die es modelliert, einen bestimmten Status mehr als einmal einnehmen kann, wodurch ein flexibleres Modell als ein sequenzieller Workflow bereitgestellt wird. Aufgrund dieses Designs ist es möglich, dass eine bestimmte State-Aktivität mehr als einmal ausgeführt werden kann, und daher muss jede Iteration in einem getrennten AEC ausgeführt werden. Dies bedeutet, dass jede State-Aktivität als Vorlage dient und jedesmal, wenn der Status erreicht wird, geklont und in einem neuen AEC ausgeführt wird. Außerdem führt die State-Aktivität auch jede der in ihr enthaltenen EventDriven-Aktivitäten in einem separaten AEC aus. Im Statusmechanismus können dieselben bereits zuvor von mir beschriebenen Lösungen verwendet werden: Aktivitätsstrukturtraversierung, freigegebener Status und Aktivitätsbindung. Der im herunterladbaren Code enthaltene Beispielworkflow AECStateMachine bietet ein konkretes Beispiel.

Regeln und Bedingungen
Entwickler haben auch dann mit dem AEC zu tun, wenn Sie Regeln oder Bedingungen schreiben. Aktivitätsbedingungen werden dazu verwendet, in einem Workflow Entscheidungen zu treffen, wie z. B. dann, wenn Sie die Bedingung für die IfElse-Aktivität definieren. In der Regel können Sie zwischen zwei Typen wählen: CodeCondition und DeclarativeRuleCondition. Eine CodeCondition ermöglicht Ihnen, in einem Ereignishandler Code zu schreiben, der ein boolesches Kennzeichen setzt, um das Ergebnis der Bedingungsüberprüfung anzuzeigen. Eine DeclarativeRuleCondition verweist auf eine Regelbedingungsanweisung, die ebenfalls einen booleschen Wert auswertet.
Beim Verwenden einer CodeCondition innerhalb eines AECs (z. B. einer IfElse-Aktivität innerhalb einer EventDriven-Aktivität in einem Statusmechanismus) stoßen Sie auf dieselben Probleme, die bereits bei Verweisen auf Aktivitäten im AEC behandelt wurden. Eine CodeCondition, die versucht, direkt auf die Aktivität „ReadLine“ zu verweisen, die in einer Aktivität „EventDriven“ gerade ihre ersten Aktionen ausführt, wird auf die Vorlage statt auf die derzeit ausgeführte Aktivität zugreifen. Schließlich verwendet eine Codebedingung Delegaten genauso wie die Ereignishandler einer Aktivität. Sie könnten beispielsweise versuchen, diesen Code zu schreiben, um Ihre Bedingung auszuwerten und „True“ zurückzugeben, wenn der Benutzer an der Konsole „Beenden“ eingibt.
if (readLine1.Text != null && readLine1.Text.StartsWith(“quit”))
{
    e.Result = true;
}
Leider wird die Text-Eigenschaft immer Null sein, da die readLine1-Instanz die Vorlage ist, und für jede Ausführung des Status wird eine geklonte Instanz verwendet werden. Ich kann dieses Problem beheben, indem ich freigegebenen Status auf Workflowebene verwende oder mithilfe des Absenderparameters und durch Auf- und Abwärtsnavigieren in der Struktur Zugriff auf die aktuelle Aktivitätsstruktur erhalte:
//get the grandparent activity (the eventdriven)
CompositeActivity grandparent = (sender as Activity).Parent.Parent;

//figure the result by finding the child ReadLine activity
e.Result = ((MSDNMag.WFActivities.ReadLine)
    grandparent.GetActivityByName(
        “readLine1”)).Text.StartsWith(“quit”);
Beachten Sie, dass ich auf die der übergeordneten Aktivität übergeordnete Aktivität (sozusagen den „Großvater“) und nicht auf die übergeordnete Aktivität (sozusagen den „Vater“) zugegriffen habe. Im Fall der IfElse-Aktivität wird die Bedingung tatsächlich durch eine IfElseBranch-Aktivität aufgerufen, die eine untergeordnete Aktivität der IfElse-Aktivität ist. Hätte ich die übergeordnete Aktivität verwendet, dann hätte ich fälschlicherweise auf die IfElse-Aktivität und nicht auf die EventDriven-Aktivität verwiesen. In diesem Fall würde ich niemals die readLine1-Aktivität mit der GetActivityByName-Methode finden. Damit will ich auf Folgendes hinaus: Wenn Sie dieses Modell der Verweisauflösung verwenden, müssen Sie wissen, welche Aktivität als Absender arbeitet, damit Sie die Aktivitätsstruktur korrekt durchlaufen können.
Die schwierigeren Probleme treten in der Regel dann auf, wenn Sie versuchen, DeclarativeRuleCondition innerhalb eines untergeordneten AECs zu verwenden oder die Aktivität Policy zum Ausführen eines Satzes von Regeln zu verwenden. Da Regelbedingungen und Richtlinien für die Workflowklasse selbst geschrieben werden, müssen die Regeln so geschrieben werden, dass sie Zugriff auf die aktuelle dynamische Instanz der Aktivitäten erhalten, auf die Sie in Ihren Regeln verweisen. In der Lage zu sein, Regeln dieses Typs zu schreiben, hängt oft von den Funktionen ab, die von der Aktivität, die den untergeordneten AEC erstellt hat, verfügbar gemacht werden. Sie könnten leicht den Fehler machen, für eine deklarative Regel eine typische Bedingung wie die folgende zu schreiben:
this.readLine2.Text.StartsWith(“quit”)
Dies ähnelt dem falschen Code im vorherigen Beispiel beim Verwenden der Codebedingung.
Vielleicht denken Sie jetzt, dass ich dieses Problem umgehen könnte, indem ich die GetActivityByName-Methode verwende, um die aktuelle Instanz abzurufen, die ich haben will:
((MSDNMag.WFActivities.ReadLine)this.GetActivityByName(
    “AECRulesInitialState”).GetActivityByName(
        “eventDrivenActivity1”).GetActivityByName(
            “readLine2”)).Text.StartsWith(“quit”)
Dies liefert leider genau dieselben Ergebnisse, da es genau dieselben Vorlagen statt der aktuellen Instanz abruft, die gerade ausgeführt wird. Um auf den derzeit ausgeführten Klon der Aktivitäten zuzugreifen, müssen Sie sich auf die Funktionen verlassen, die durch die Aktivität, die den neuen AEC erstellt hat, verfügbar gemacht werden. So stellt beispielsweise die State-Aktivität eine Methode namens „GetDynamicActivity“ zur Verfügung, die es Ihnen ermöglicht, die derzeit ausgeführte Instanz einer untergeordneten Aktivität abzufragen. Mithilfe dieser Methode kann ich jetzt eine deklarative Regel schreiben, die die aktuelle Instanz der ReadLine-Aktivität korrekt identifiziert, damit ich meine Entscheidung richtig treffen kann:
((MSDNMag.WFActivities.ReadLine)
    ((System.Workflow.Activities.StateActivity)
        this.GetDynamicActivity(
            “AECRulesInitialState”)).GetDynamicActivity(
                 “eventDrivenActivity1”).GetActivityByName(
                     “readLine2”)).Text.StartsWith(“quit”)
In dieser deklarativen Regel verwende ich zuerst die GetDynamicActivity-Methode, um die derzeit ausgeführte Instanz der Statusaktivität „AECRulesInitialState“ abzurufen. Danach wandle ich das Ergebnis in eine State-Aktivität um, die es mir ermöglicht, erneut die GetDynamicActivity-Methode zu verwenden, um auf die derzeit ausgeführte Instanz der Aktivität „EventDriven“ zuzugreifen. Sobald ich einen Verweis auf die Aktivität „EventDriven“ besitze, muss ich mir keine Sorgen mehr um untergeordnete AECs machen und kann daher die GetActivityByName-Methode verwenden, um einen Verweis auf die Aktivität „ReadLine“ und ihre Text-Eigenschaft zu erhalten.
Dasselbe Verfahren kann auch für andere Aktivitäten sowohl in Statuscomputerworkflows als auch in sequenziellen Workflows verwendet werden. So besitzt beispielsweise die While-Aktivität eine Eigenschaft namens „DynamicActivity“, die dazu verwendet werden kann, in Ereignishandlern oder deklarativen Regelbedingungen auf den aktuellen Klon der Aktivitätsvorlage zuzugreifen. Der herunterladbare Code beinhaltet den Beispielstatusmechanismus sowie einen sequenziellen Workflow, die die Verwendung der While-Aktivität demonstrieren.

Benutzerdefinierte Aktivitäten
Die bisher erörterten Themen sind für alle Workflowentwickler relevant und für Ihre Fähigkeit, Workflows zu erstellen, unverzichtbar. Die Ersteller von Aktivitäten müssen aber auch wissen, wann und wie sie AECs erstellen und verwalten können, wenn sie zusammengesetzte Aktivitäten entwickeln.
Um zu ermitteln, ob Ihre benutzerdefinierte zusammengesetzte Aktivität einen neuen AEC erstellen muss, stellen Sie sich einfach die Frage „Wird meine Aktivität ihre untergeordneten Aktivitäten mehr als einmal ausführen?“. Wenn die Antwort „Ja“ lautet, müssen Sie für jede Iteration der untergeordneten Aktivitäten einen neuen AEC erstellen. Betrachten Sie zwei Aktivitäten, die in der Base Activity Library von Windows Workflow Foundation enthalten sind: Parallel und While. Ich habe Ihnen bereits gezeigt, dass die While-Aktivität iterativ ihre untergeordnete Aktivität ausführt und daher jede Iteration in einem neuen AEC ausführen muss. Die Aktivität „Parallel“ ermöglicht Ihnen, mehrere Ausführungszweige zu definieren, und plant jeden Zweig so, dass die Zweige verschachtelt ausgeführt werden. Obwohl die Aktivität „Parallel“ mehrere Sequenzen untergeordneter Aktivitäten ausführt, führt sie jede untergeordnete Aktivität nur ein einziges Mal aus. Auch wenn die Definitionen von zwei Zweigen identisch zu sein scheinen, handelt es sich um separate Instanzen der Aktivitätstypen und werden sie unabhängig voneinander ausgeführt.
Um den Vorgang des Schreibens von Aktivitäten dieses Typs zu untersuchen, werde ich eine ForEach-Aktivität erstellen, die an einer Sammlung von Datenobjekten iteriert wird und ihre untergeordnete Aktivität an jedem Element der Sammlung genau einmal ausführt. Im herunterladbaren Code für diesen Artikel steht der komplette Beispielcode zur Verfügung, aber ich werde mich hier speziell auf den Code konzentrieren, der sich auf die Verwaltung der AECs bezieht.
Zunächst brauche ich drei Abhängigkeitseigenschaften, die dabei helfen, Status und Daten zu verwalten: CurrentIteration (eine private Eigenschaft zum Verwalten des Index), Items (die Sammlung der Daten, an denen die Iterationen durchgeführt werden) und DataItem (eine angefügte Eigenschaft, um das Element aus der Sammlung an die untergeordnete Aktivität zu übergeben, die ausgeführt wird). Mit diesen Eigenschaften hat es in Bezug auf den AEC nichts Besonderes auf sich; sie stellen lediglich den Status dar, den Sie verwalten müssen, um mehrere Iterationen der untergeordneten Aktivität auszuführen.
Als Nächstes müssen Sie an der Execute-Methode der ForEach-Aktivität arbeiten, die für den Start der Ausführung der untergeordneten Aktivität zuständig ist:
if (EnabledActivities.Count == 0 || Items == null || Items.Count == 0)
    return ActivityExecutionStatus.Closed;

CurrentIteration = 0;

ExecuteIteration(executionContext);

return ActivityExecutionStatus.Executing;
Es ist wichtig, rasch zu überprüfen, dass Arbeit in Form einer untergeordneten Aktivität und einer Sammlung von Daten vorhanden ist, an denen die Iterationen durchgeführt werden können. Ist dies nicht der Fall, kann die Aktivität geschlossen und die nächste Aktivität im Workflow ausgeführt werden. Wenn Daten und eine untergeordnete Aktivität vorhanden sind, dann wird der Iterationsindex initialisiert, und ich rufe die ExecuteIteration-Methode auf, die den Punkt darstellt, an dem die Arbeit beginnt, interessant zu werden. Da ich die auszuführende untergeordnete Aktivität plane, kehre ich zum Schluss zu einem ausführenden Status zurück.
Die ExecuteIteration-Methode ist für die Verwaltung der Erstellung eines neuen AECs zuständig, um die untergeordnete Aktivität auszuführen:
Activity act = EnabledActivities[0];

ActivityExecutionContext newContext = 
    parentCtx.ExecutionContextManager.CreateExecutionContext(act);

newContext.Activity.RegisterForStatusChange(
    Activity.ClosedEvent, this);
newContext.Activity.SetValue(
    ForEach.DataItemProperty, Items[CurrentIteration]);

newContext.ExecuteActivity(newContext.Activity);
Der erste Schritt besteht darin, mithilfe des ActivityExecutionContextManager (AECManager) einen neuen ActivityExecutionContext zu erstellen. Der AECManager ist in jedem beliebigen AEC verfügbar und wird dazu verwendet, die Erstellung und den Abschluss untergeordneter AECs zu steuern. Wenn der neue AEC erstellt wird, klont der AECManager die Aktivität, die übergeben wurde, und verwendet die neue Aktivität bei der Ausführung. Aus diesem Grund verwende ich in den folgenden Schritten die Activity-Eigenschaft im neuen AEC, um auf die Aktivität zu verweisen, wenn Statusänderungen registriert werden und der Kontext angewiesen wird, die Aktivität auszuführen. Der einzige weitere Punkt, auf den hier hingewiesen werden sollte, ist der Umstand, dass ich die DataItemProperty in der untergeordneten Aktivität auf die aktuellen Daten aus der Sammlung einstelle, damit die Daten der dynamisch ausgeführten Aktivität zur Verfügung stehen.
Beachten Sie, dass ich zum Erstellen des AECs eine Aktivität übergebe, die im AEC als Stammaktivität dienen wird. Dieses Konzept muss unbedingt verstanden werden, da es das Design Ihrer Aktivität bestimmt, wenn Sie wissen, dass Sie einen neuen AEC basierend auf einer einzigen Aktivität erstellen müssen. Die meisten Aktivitäten, die einen neuen AEC erstellen, wie z. B. die Aktivitäten „While“ und „Replicator“, gestatten nur eine einzige untergeordnete Aktivität. Auf den ersten Blick mag dies wie eine Beschränkung erscheinen, aber Sie können leicht als einzige untergeordnete Aktivität eine Sequence-Aktivität hinzufügen und danach dieser Sequence-Aktivität beliebig viele untergeordnete Aktivitäten hinzufügen. Es sind zwei Schritte erforderlich, um sicherzustellen, dass eine einzige untergeordnete Aktivität vorhanden ist. Der erste Schritt besteht darin, einen benutzerdefinierten Validator zu erstellen, der sicherstellt, dass Ihre Aktivität nur eine einzige untergeordnete Aktivität besitzt. Der zweite Schritt besteht in der Erstellung eines Designers, der nicht zulässt, dass mehr als eine einzige Aktivität zur ForEach-Aktivität hinzugefügt wird. Beide Verfahren sind im Beispielcode für die ForEach-Aktivität enthalten. Eine ausführlichere Erläuterung von Designern und Validatoren finden Sie in meinem Artikel vom Dezember 2006 unter dem Titel „Windows Workflow: Erstellen benutzerdefinierter Aktivitäten, um die Reichweite Ihrer Workflows zu vergrößern“.
Der letzte Schritt zur Verwaltung des AECs besteht darin, das Closed-Ereignis der untergeordneten Aktivität zu verarbeiten, um den Kontext abzuschließen. Der Absenderparameter für diesen Delegaten ist der AEC für meine zusammengesetzte Aktivität und kann dazu verwendet werden, die Aktivität zu steuern und Zugriff auf den AECManager zu erhalten, um untergeordnete AECs zu verwalten. ActivityExecutionStatusChangedEventArgs enthält einen Verweis auf die Aktivität, die geschlossen wurde. Dieser Verweis kann dann dazu verwendet werden, mithilfe der GetExecutionContext-Methode von AECManager den richtigen AEC für diese Aktivität abzurufen. Sobald ich einen Verweis zum AEC für die Aktivität erworben habe, schließe ich diesen AEC mithilfe von AECManager. Es ist wichtig, dass nicht nur alle untergeordneten Aktivitäten geschlossen, sondern auch alle untergeordneten AECs abgeschlossen werden. Sobald der AEC abgeschlossen wurde, kann ich prüfen, ob weitere Datenelemente vorhanden sind. Ist dies der Fall, starte ich eine weitere Iteration; ist dies nicht der Fall, schließe ich meine Aktivität:
ActivityExecutionContext ctx = sender as ActivityExecutionContext;
            
e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, this);
            
ActivityExecutionContext childContext = 
    ctx.ExecutionContextManager.GetExecutionContext(e.Activity);
ctx.ExecutionContextManager.CompleteExecutionContext(childContext);

if (++CurrentIteration >= Items.Count) ctx.CloseActivity();
else ExecuteIteration(ctx);
Die CompleteExecutionContext-Methode besitzt eine Überladung, die einen booleschen Parameter verwendet, um anzuzeigen, ob der Workflowstatus an diesem Punkt beibehalten werden sollte. Standardmäßig lautet dieser Wert „False“. Wenn jedoch untergeordnete Aktivitäten, die die ICompensatable-Schnittstelle implementieren, abgeschlossen wurden, wird der AEC unabhängig von dem für diesen Parameter übergebenen Wert beibehalten. Diese Überladung ermöglicht Ihnen, die Eingabe bereitzustellen, ob der AEC aufgrund der Anforderungen Ihrer Aktivität beibehalten werden oder der Status zu einem späteren Zeitpunkt verfügbar sein muss.
Zum Schluss muss in meiner Aktivität die Möglichkeit unterstützt werden, dass andere Codes einen Verweis zur aktuellen Instanz der untergeordneten Aktivität abrufen können, die gerade ausgeführt wird. Um dies zu erreichen, füge ich in der ForEach-Aktivität eine DynamicActivity-Eigenschaft hinzu und verwende die GetDynamicActivities-Methode der CompositeActivity-Basisklasse. Von dieser Methode wird eine Aktivität verwendet und ein Array der dynamischen Instanzen dieser Aktivität zurückgegeben. Da in diesem Fall immer nur eine einzige dynamische Instanz vorhanden sein wird, kann ich stets das erste Element im Array zurückgeben:
public Activity DynamicActivity
{
    get 
    {
        if (EnabledActivities.Count == 0) return null;
        Activity[] activities = 
            GetDynamicActivities(EnabledActivities[0]);
        if (activities.Length > 0) return activities[0];
        return null;
    }
}
Es ist wichtig, daran zu denken, dass andere Aspekte der Entwicklung zusammengesetzter Aktivitäten ebenfalls eine Handhabung des AECs erfordern könnten, wie zum Beispiel die Abbruch- und Fehlerbehandlung. In diesen Fällen ist das Muster für den Zugriff auf den richtigen AEC mit dem hier vorgeführten Muster identisch, und die Schritte, die Sie durchführen müssen, hängen von Ihrer Aktivität ab.

Schlussbemerkung
Es ist wichtig, dass Workflowentwickler verstehen, wie der AEC funktioniert und wann es notwendig ist, die Verwendung des AECs in Betracht zu ziehen. Gleichzeitig müssen Entwickler auch wissen, wie sie Workflows, Aktivitäten und Regeln programmieren können, um mit dem AEC umzugehen. Das Schreiben einer einfachen benutzerdefinierten zusammengesetzten Aktivität ist ein gutes Verfahren, um ein Verständnis dafür zu gewinnen, wie andere Aktivitäten möglicherweise ihren AEC verwalten. Ich empfehle diese Methode den meisten Workflowentwicklern als Lernverfahren.


Senden Sie Ihre Fragen und Kommentare an  mmnet30@microsoft.com.


Matt Milner ist ein unabhängiger Softwareberater, der sich auf Microsoft-Technologien wie .NET, Webdienste, Windows Workflow Foundation, Windows Communication Foundation und BizTalk Server spezialisiert hat. Als Ausbilder für Pluralsight leitet er Kurse zu Workflow, BizTalk Server und Windows Communication Foundation. Matt lebt mit seiner Frau Kristen und seinen zwei Söhnen in Minnesota. Sie erreichen Matt über seinen Blog unter pluralsight.com/blogs/matt.

Page view tracker