Freigeben über


Dieser Artikel wurde maschinell übersetzt.

Windows Foundation 4

Erstellung angepasster Control Flow-Aktivitäten in WF 4

Leon Welicki

Ablaufsteuerung bezieht sich auf die Art und Weise, in der die einzelnen Anweisungen in einem Programm organisiert und ausgeführt werden. In Windows Workflow Foundation 4 (WF-4) bestimmen Steuerelement Fluss Aktivitäten die Semantik der Ausführung der untergeordneten Aktivitäten. Einige Beispiele für Aktivität Toolbox enthalten Sequence, parallel, wenn WF-4, ForEach, Kommissionierung, Flussdiagramm und wechseln, u. a..

Die WF-Laufzeit sein nicht erstklassige eine Ablaufsteuerung, wie z. B. Zeichenfolge oder Parallel. Aus der Perspektive ist alles nur Aktivitäten. Die Common Language Runtime erzwingt nur einige einfachen Regeln (z. B. “ eine Aktivität kann nicht abgeschlossen, wenn alle untergeordneten Aktivitäten noch ausgeführt werden ”). WF-Ablaufsteuerung Grundlage Hierarchie – ein WF-Programm ist eine Struktur von Aktivitäten.

Fluss Steuerungsoptionen in WF 4 sind nicht auf die Aktivitäten, die im Lieferumfang des Frameworks beschränkt. Schreiben eigener und deren Verwendung in Kombination mit Energieschemas im Feld werden können, und das ist, was in diesem Artikel beschrieben wird. Sie erfahren, wie ein eigenes Steuerelement folgt einen Ansatz “ crawlen, durchlaufen, führen Sie ” Datenfluss-Aktivitäten schreiben können: Wir werden mit einer sehr einfachen Steuerelements Fluss Aktivität starten und Vielfältigkeit hinzufügen, während wir endet eine neue und nützliches Steuerelement Datenfluss-Aktivität.  Der Quellcode für alle unsere Beispiele ist zum Download zur Verfügung.

Doch zuerst let’s zunächst einige grundlegende Konzepte zu Aktivitäten, einige gemeinsame Boden festzulegen.

Aktivitäten

Aktivitäten sind die grundlegende Einheit der Ausführung in einem WF-Programm;ein Workflow-Programm ist eine Struktur von Aktivitäten, die durch die WF-Laufzeit ausgeführt werden. WF 4 enthält mehr als 35 Aktivitäten, einen umfassenden Satz an, die zum Modellieren von Prozessen, oder erstellen Sie neue Aktivitäten verwendet werden kann. Einige dieser Aktivitäten steuern die Semantik wie andere Aktivitäten (z. B. Zeichenfolge, Flussdiagramm, Parallel und ForEach) ausgeführt werden und werden als Aktivitäten für zusammengesetzte bezeichnet. Andere führen einen einzelnen atomaren Vorgang (WriteLine, InvokeMethod usw.). Wir bezeichnen diese Endknoten Aktivitäten.

WF-Aktivitäten werden als CLR-Typen implementiert und als solche Sie von anderen vorhandenen Typen abgeleitet. Sie können Aktivitäten mithilfe von WF-Designer visuell und deklarativ oder imperativ Schreiben von CLR-Code schreiben. Die Basistypen verfügbar, um eine eigene benutzerdefinierte Aktivität erstellen, werden in die Typhierarchie Aktivitäten in Abbildung 1 definiert. Dieser Typhierarchie eine ausführliche Erläuterung finden Sie in der MSDN Library unter msdn.microsoft.com/library/dd560893-.

image: Activity Type Hierarchy

Abbildung 1 Aktivität geben die Hierarchie

In diesem Artikel konzentriere ich mich auf Aktivitäten, die von NativeActivity, die Basisklasse ableiten, die Zugriff auf die vollständige Spektrum von WF-Laufzeit ermöglicht. Steuerelement Fluss Aktivitäten sind zusammengesetzte Aktivitäten, die aus dem NativeActivity Typ abgeleitet, werden da Sie die WF-Laufzeit interagieren müssen. Dies wird am häufigsten planen, andere Aktivitäten (z. B. Zeichenfolge, Parallel oder Flussdiagramm), jedoch kann auch enthalten Implementieren benutzerdefinierter Abbruch CancellationScope oder Kommissionierung, Erstellen von Textmarken mit empfangen und die Beibehaltung der Verwendung persistenter.

Aktivität-Datenmodell definiert eine klare Modell für die Logik zu Daten, die beim Erstellen und Verwenden von Aktivitäten. Daten werden mithilfe von Argumenten und Variablen definiert. Argumente sind die Bindung Terminals einer Aktivität und seine öffentliche Signatur hinsichtlich der Daten zu der Aktivität (Eingabeargumente) übergeben werden können und welche Daten von der Aktivität zurückgegeben werden, Abschluss seiner Ausführung (Ausgabe Argumente) definieren. Variablen stellen die temporäre Speicherung von Daten dar.

Aktivität-Autoren verwenden Argumente, um die Art und Weise Datenflüsse ein-und Auschecken von einer Aktivität zu definieren, und verwenden Sie Variablen in zwei Möglichkeiten:

  • Eine Auflistung Benutzer bearbeitet werden Variablen auf die Aktivitätsdefinition einer verfügbar machen, die zum Freigeben von Variablen zwischen mehreren Aktivitäten (z. B. eine Variables-Auflistung in der Reihenfolge und Flussdiagramm) verwendet werden kann.
  • Um den internen Zustand einer Aktivität zu modellieren.

Workflow Authors verwenden Argumente, um die Aktivitäten in der Umgebung zu binden, durch das Schreiben von Ausdrücken, und deklarieren Sie Variablen in den verschiedenen Gültigkeitsbereichen zum Freigeben von Daten zwischen Aktivitäten Workflows. Kombinieren Variablen und Argumente zusammen, um eine vorhersagbare Modell der Kommunikation zwischen Aktivitäten zu ermöglichen.

Nun, da ich einige grundlegende Aktivität Grundlagen behandelt haben, starten Sie let’s mit der ersten Steuerelement Fluss Aktivität.

Eine Simple Control Flow-Aktivität

Zunächst wird eine sehr einfaches Steuerelement Fluss Aktivität namens ExecuteIfTrue erstellt. Es ist nicht viel zu dieser Aktivität: Es wird eine Aktivität enthaltene ausgeführt, wenn eine Bedingung true ist. WF 4 bietet eine If-Aktivität, die dann enthält und Else untergeordneten Aktivitäten;oft wir nur die Then bereitstellen möchten, und die Else ist einfach Aufwand. Für diese Fälle wollen wir eine Aktivität, die ausgeführt, eine andere Aktivität basierend auf dem Wert einer booleschen Bedingung wird.

Hier ist, wie diese Aktivität arbeiten soll:

  • Der Benutzer Aktivitäten muss eine boolesche Bedingung angeben. Dieses Argument ist erforderlich.
  • Der Aktivität Benutzer kann keinen Text bereitstellen – die Aktivität ausgeführt werden, wenn die Bedingung wahr ist.
  • Zur Ausführungszeit: Wenn die Bedingung erfüllt ist, und der Text nicht null ist, führen Sie den Textkörper.

Hier ist eine Implementierung für eine ExecuteIfTrue-Aktivität, die auf diese Weise verhält sich:

public class ExecuteIfTrue : NativeActivity
{
  [RequiredArgument]
  public InArgument<bool> Condition { get; set; }

  public Activity Body { get; set; }

  public ExecuteIfTrue() { }  

  protected override void Execute(NativeActivityContext context)
  {            
    if (context.GetValue(this.Condition) && this.Body != null)
      context.ScheduleActivity(this.Body);
  }
}

Dieser Code ist sehr einfach, aber es hier entspricht das Auge ist mehr. ExecuteIfTrue wird eine untergeordnete Aktivität ausgeführt, wenn eine Bedingung erfüllt, ist damit eine andere Aktivität geplant werden muss. Daher ist es von NativeActivity, da es die WF-Laufzeit zum Planen von untergeordneten Elementen interagieren muss.

Nachdem Sie die Basisklasse für eine Aktivität entschieden haben, müssen Sie seine öffentliche Signatur definieren. In ExecuteIfTrue besteht aus einer booleschen Eingabeargument vom Typ InArgument <bool>mit der Bedingung, der die Bedingung ausgewertet werden, und eine Eigenschaft vom Typ Aktivität mit dem Namen für Text mit der Aktivität ausgeführt werden, wenn die Bedingung wahr ist. Das Argument Bedingung ist mit dem RequiredArgument-Attribut versehen, die die WF-Laufzeit anzeigt, dass Sie mit einem Ausdruck festgelegt werden muss. Die WF-Laufzeit erzwingt diese Überprüfung beim Vorbereiten der Ausführung der Aktivität:

[RequiredArgument]
public InArgument<bool> Condition { get; set; }

public Activity Body { get; set; }

Der interessanteste Teil des Codes in dieser Aktivität wird die Execute-Methode, wobei die “ Aktion ” geschieht. Alle NativeActivities muss überschreiben diese Methode. Die Execute-Methode empfängt eine NativeActivityContext-Argument, das unsere Punkt der Interaktion mit der WF-Laufzeit als Autoren der Aktivität ist. Im ExecuteIfTrue, wird in diesem Kontext verwendet, um den Wert des Arguments Bedingung abzurufen (context.GetValue(this.Condition)) und den Text mit der Methode ScheduleActivity planen. Beachten Sie, dass ich nicht AusführenZeitplan-, und sagen. Die WF-Laufzeit wird die Aktivitäten nicht sofort ausgeführt;Stattdessen fügt er diese an eine Liste von Arbeitsaufgaben für die Ausführung geplant werden:

protected override void Execute(NativeActivityContext context)
{
    if (context.GetValue(this.Condition) && this.Body != null)
        context.ScheduleActivity(this.Body);
}

Beachten Sie auch, dass der Typ das Create Set Einsatz Muster folgen entwickelt wurde. Die XAML-Syntax basiert auf diesem Muster für das Entwerfen von Typen, wobei es sich bei dem Typ um einen öffentlichen Standardkonstruktor und öffentliche Lese-/Schreibeigenschaften ist. Dies bedeutet, der XAML-Serialisierung geeignet sein wird.

Der folgende Codeausschnitt zeigt, wie diese Aktivität verwenden. In diesem Beispiel wird der aktuelle Tag Samstag, Schreiben Sie die Zeichenfolge “ Rest! ” auf der Konsole ausgegeben:

var act = new ExecuteIfTrue
{
  Condition = new InArgument<bool>(c => DateTime.Now.DayOfWeek == DayOfWeek.Tuesday),
  Body = new WriteLine { Text = "Rest!" }
};

WorkflowInvoker.Invoke(act);

Die erste Steuerelement Fluss Aktivität wurde 15 Zeilen Code erstellt. Aber nicht durch die Einfachheit des Codes betrogen werden – es ist tatsächlich ein voll funktionsfähiges Steuerelement Fluss Aktivität!

Planen mehrere untergeordnete Elemente

Die nächste Herausforderung ist eine vereinfachte Version der Sequence-Aktivität zu schreiben. Das Ziel dieser Übung besteht darin, erfahren, wie eine Steuerelement Fluss Aktivität schreiben, die mehrere untergeordnete Aktivitäten geplant und führt in mehrere folgen. Diese Aktivität ist fast Ihre Funktion entspricht der Zeichenfolge, die im Lieferumfang des Produkts.

Hier ist, wie diese Aktivität arbeiten soll:

  • Der Aktivität Benutzer muss es sich um eine Auflistung der untergeordneten Elemente über die Eigenschaft Aktivitäten sequenziell ausgeführt werden bereitstellen.
  • Zur Ausführungszeit:
    • Die Aktivität enthält eine interne Variable mit dem Index des letzten Elements in der Auflistung, die ausgeführt wurde.
    • Wenn in der Auflistung der untergeordneten Elemente, Planen Sie das erste untergeordnete Element.
    • Wenn das untergeordnete Element abgeschlossen ist:
      • Erhöhen Sie der Index des letzten Elements ausgeführt.
      • Wenn der Index noch innerhalb der Grenzen der Children-Auflistung ist, Planen Sie das nächste untergeordnete Element.
      • Wiederholen.

Der Code in Abbildung 2 implementiert eine SimpleSequence-Aktivität, die genau wie beschrieben verhält.

Abbildung 2 der Aktivität SimpleSequence

public class SimpleSequence : NativeActivity
{
  // Child activities collection
  Collection<Activity> activities;
  Collection<Variable> variables;

  // Pointer to the current item in the collection being executed
  Variable<int> current = new Variable<int>() { Default = 0 };
     
  public SimpleSequence() { }

  // Collection of children to be executed sequentially by SimpleSequence
  public Collection<Activity> Activities
  {
    get
    {
      if (this.activities == null)
        this.activities = new Collection<Activity>();

      return this.activities;
    }
  }

  public Collection<Variable> Variables 
  { 
    get 
    {
      if (this.variables == null)
        this.variables = new Collection<Variable>();

      return this.variables; 
    } 
  }

  protected override void CacheMetadata(NativeActivityMetadata metadata)
  {
    metadata.SetChildrenCollection(this.activities);
    metadata.SetVariablesCollection(this.variables);
    metadata.AddImplementationVariable(this.current);
  }

  protected override void Execute(NativeActivityContext context)
  {
    // Schedule the first activity
    if (this.Activities.Count > 0)
      context.ScheduleActivity(this.Activities[0], this.OnChildCompleted);
  }

  void OnChildCompleted(NativeActivityContext context, ActivityInstance completed)
  {
    // Calculate the index of the next activity to scheduled
    int currentExecutingActivity = this.current.Get(context);
    int next = currentExecutingActivity + 1;

    // If index within boundaries...
if (next < this.Activities.Count)
    {
      // Schedule the next activity
      context.ScheduleActivity(this.Activities[next], this.OnChildCompleted);

      // Store the index in the collection of the activity executing
      this.current.Set(context, next);
    }
  }
}

Erneut, eine voll funktionsfähiges Steuerelement Fluss Aktivität in nur wenigen Zeilen des Codes geschrieben wurden – in diesem Fall ungefähr 50 Zeilen. Der Code ist einfach, aber es werden einige interessante Konzepte vorgestellt.

SimpleSequence wird eine Auflistung von untergeordneten Aktivitäten in der angegebenen Reihenfolge ausgeführt, damit andere Aktivitäten geplant werden muss. Daher ist es von NativeActivity da benötigt für die Interaktion mit der Common Language Runtime beim Planen der untergeordneten Elemente.

Der nächste Schritt ist die öffentliche Signatur für SimpleSequence definieren. In this case it consists of a collection of activities (of type Collection<Activity>) exposed through the Activities property, and a collection of variables (of type Collection<Variable>) exposed through the Variables property. Variablen können die Freigabe von Daten zwischen alle untergeordneten Aktivitäten. Beachten Sie, dass diese Eigenschaften nur “ Getter ”, verfügbar gemacht (siehe Abbildung 3 ), die Auflistungen mit “ lazy Instanziierung ”, Ansatz so ergibt sich Zugriff auf diese Eigenschaften wird nie in einen null-Verweis. Dadurch wird diese Eigenschaften mit dem Muster Create Set Einsatz kompatibel.

Abbildung 3 eine verzögerte Instanziierung Ansatz

public Collection<Activity> Activities
{
  get
  {
    if (this.activities == null)
      this.activities = new Collection<Activity>();

    return this.activities;
  }
}

public Collection<Variable> Variables 
{ 
  get 
  {
    if (this.variables == null)
      this.variables = new Collection<Variable>();

    return this.variables; 
  } 
}

Es ist ein privater Member in der Klasse, die nicht Teil der Signatur ist: einer Variablen <int>mit der Bezeichnung “ aktuelle ” enthält, der den Index der Aktivität ausgeführt wird:

// Pointer to the current item in the collection being executed
Variable<int> current = new Variable<int>() { Default = 0 };

Da diese Informationen in die interne Ausführungsstatus für SimpleSequence ist, möchten Sie privat zu halten und nicht für Benutzer von SimpleSequence verfügbar zu machen. Sie möchten es gespeichert und wiederhergestellt werden, wenn die Aktivität beibehalten wird. Sie verwenden eine ImplementationVariable dafür.

Implementierung Variablen sind Variablen, die intern für eine Aktivität. Sie sind vom Autor Aktivität nicht die Aktivitäten der Benutzer verbraucht werden soll. Implementierung von Variablen werden beibehalten, wenn die Aktivität gespeichert und wiederhergestellt wird, wenn Sie die Aktivität erneut geladen wird, ohne dass alle unsere Teil arbeiten, ist. Um dies zu löschen – und das Sequence-Beispiel – wenn eine Instanz des SimpleSequence beibehalten wird, wenn er reaktiviert wird “ vergessen ” den Index der letzten Aktivität, die ausgeführt wurde.

Implementierung von Variablen kann nicht automatisch die WF-Laufzeit kennen. Wenn Sie eine ImplementationVariable in einer Aktivität verwenden möchten, müssen Sie explizit die WF-Laufzeit darüber informieren. Dies erfolgt während der Ausführung der CacheMetadata-Methode.

Trotz seines Namens eher frightening ist CacheMetadata nicht schwer. Im Prinzip ist es wirklich einfach: Es ist die Methode, in denen eine Aktivität “ selbst an die Common Language Runtime führt ”. Stellen Sie sich über die If-Aktivität für einen Moment. Diese Aktivität würde in CacheMetadata sagen: “ Hallo, ich habe die If-Aktivität, und haben eine Eingabeargument namens Bedingung und zwei untergeordnete Elemente: Anschließend und Else. ” In dem Fall SimpleSequence ist SimpleSequence sagen: “ Hallo, ich SimpleSequence und habe ich eine Auflistung von untergeordneten Aktivitäten, die eine Auflistung von Variablen und eine Implementierung Variable ”. Es ist nicht mehr als die in den Code SimpleSequence CacheMetadata:

protected override void CacheMetadata(NativeActivityMetadata metadata)
{
  metadata.SetChildrenCollection(this.activities);
  metadata.SetVariablesCollection(this.variables);
  metadata.AddImplementationVariable(this.current);
}

Die Standardimplementierung der CacheMetadata verwendet Reflektion, um die von der Aktivität dieser Daten abgerufen werden. In dem Beispiel ExecuteIfTrue ich nicht CacheMetadata implementieren und auf Öffentliche Member entsprechend der Standardimplementierung verlassen. Für SimpleSequence im Gegensatz dazu muss ich es implementiert werden, da die Standardimplementierung “ über den Wunsch, meinen Implementierung Variablen verwenden erraten kann nicht ”.

Der nächste interessante Teil des Codes in dieser Aktivität wird die Execute-Methode. Wenn Sie Aktivitäten in der Auflistung vorhanden sind, erfahren Sie in diesem Fall die WF-Laufzeit: “ Bitte führen Sie die erste Aktivität in der Auflistung von Aktivitäten aus und wenn Sie fertig sind, rufen Sie die Methode OnChildCompleted. ” Sagen Sie dies in WF-Terminologie NativeActivityContext.ScheduleActivity verwenden. Beachten Sie, dass bei der Planung einer Aktivität ein zweites Argument angegeben wird, das eine CompletionCallback ist. Einfach ausgedrückt ist dies eine Methode, die aufgerufen wird, sobald die Aktivitätsausführung der abgeschlossen ist. Auch hier ist es wichtig, den Unterschied zwischen Planung und Ausführung berücksichtigen. Die CompletionCallback wird nicht aufgerufen werden, wenn die Aktivität geplant ist;Es wird aufgerufen, wenn die Aktivitätsausführung von geplanten abgeschlossen wurde:

protected override void Execute(NativeActivityContext context)
{
  // Schedule the first activity
  if (this.Activities.Count > 0)
    context.ScheduleActivity(this.Activities[0], this.OnChildCompleted);
}

OnChildCompleted-Methode ist die interessanteste Teil dieser Aktivität vom Standpunkt der lernen, und es ist tatsächlich der Hauptgrund habe ich SimpleSequence in diesem Artikel enthalten. Diese Methode ruft die nächste Aktivität in der Auflistung und plant, es. Wenn das nächste untergeordnete Element geplant ist, eine CompletionCallback bereitgestellt, die in diesem Fall auf die gleiche Weise verweist. Auf diese Weise nach Abschluss einer untergeordneten wird diese Methode erneut ausgeführt, suchen Sie nach der nächsten untergeordneten und ausgeführt werden. Ausführung geschieht natürlich in Impulse oder folgen. Da Workflows gespeichert und aus dem Arbeitsspeicher entladen werden können, kann es eine große Zeitdifferenz zwischen zwei Impulse der Ausführung. Darüber hinaus können Sie diese Impulse in verschiedenen Threads, Prozesse oder sogar Computer ausgeführt werden (wie neu gespeicherte Instanzen eines Workflows in einem anderen Prozess oder Computer geladen werden können). Lernen, wie Sie mehrere Impulse der Ausführung zu programmieren, ist eine der größten Herausforderungen in zunehmend ein Experte Steuerelementautor Fluss Aktivität:

void OnChildCompleted(NativeActivityContext context, ActivityInstance completed)
{
  // Calculate the index of the next activity to scheduled
  int currentExecutingActivity = this.current.Get(context);
  int next = currentExecutingActivity + 1;

  // If index within boundaries...
if (next < this.Activities.Count)
  {
    // Schedule the next activity
    context.ScheduleActivity(this.Activities[next], this.OnChildCompleted);

    // Store the index in the collection of the activity executing
    this.current.Set(context, next);
  }
}

Der folgende Codeausschnitt zeigt, wie diese Aktivität verwenden. In diesem Beispiel habe ich drei Zeichenfolgen an die Konsole ("Hallo," schreiben."Workflow"und "!"):

var act = new SimpleSequence()
{
  Activities = 
  {
    new WriteLine { Text = "Hello" },
    new WriteLine { Text = "Workflow" },
    new WriteLine { Text = "!" }
  }
};

WorkflowInvoker.Invoke(act);

Ich habe meine eigene SimpleSequence erstellt! Jetzt ist es an der Zeit auf die nächste Herausforderung zu verschieben.

Ein neues Steuerelement Bewegungsdimension Muster implementieren

Anschließend werde ich eine komplexes Steuerelement Fluss Aktivität erstellen. Wie bereits erwähnt, sind Sie nicht auf das Steuerelement Fluss Aktivitäten im Lieferumfang von WF 4 beschränkt. Dieser Abschnitt beschreibt die zum Erstellen Ihrer eigenen Steuerelement Fluss Aktivität einen Fluss Steuerelementmuster unterstützen, die keine Out-of-the-Box von WF 4 unterstützt hat.

Die neue Steuerelement Fluss-Aktivität wird die Reihe aufgerufen. Ihr Ziel ist einfach: zum Bereitstellen einer Sequenz mit Unterstützung für GoTos, wo die nächste Aktivität ausgeführt werden kann bearbeitet werden entweder explizit durch innerhalb des Workflows (über eine GoTo-Aktivität) oder vom Host (durch Fortsetzen einer bekannten Textmarke).

Um diese neuen Ablaufsteuerung zu implementieren, müssen ich zwei Aktivitäten erstellen: Datenreihen, eine zusammengesetzte Aktivität, die enthält einer Sammlung von Aktivitäten und sequenziell ausgeführt wird (aber springt zu einem beliebigen Element in der Sequenz ermöglicht) und GoTo, eine Endknoten-Aktivität, die innerhalb der Reihe Verwendung müssen explizit die Sprünge zu modellieren.

Recap, werde ich die Ziele und Anforderungen für das benutzerdefinierte Steuerelement Aktivität durchlaufen:

  1. Es ist eine Abfolge von Aktivitäten.
  2. Es kann GoTo-Aktivitäten (an beliebige Tiefe) enthalten, die den Zeitpunkt der Ausführung alle direkt untergeordneten von der Serie ändern.
  3. Sie können GoTo Nachrichten von außerhalb (z. B. von einem Benutzer) empfangen, die den Zeitpunkt der Ausführung, mit der die Reihe alle direkt untergeordneten ändern können.

Ich beginne durch Implementieren der Series-Aktivität. Hier ist die Semantik der Ausführung einfach ausgedrückt:

  • Der Aktivität Benutzer muss es sich um eine Auflistung der untergeordneten Elemente über die Eigenschaft Aktivitäten sequenziell ausgeführt werden bereitstellen.
  • In der Execute-Methode:
    • Erstellen Sie eine Textmarke für die GoTo in einer Weise, die untergeordneten Aktivitäten zur Verfügung steht.
    • Die Aktivität enthält eine interne Variable mit der Aktivitätsinstanz ausgeführt wird.
    • Wenn in der Auflistung der untergeordneten Elemente, Planen Sie das erste untergeordnete Element.
    • Wenn das untergeordnete Element abgeschlossen ist:
      • Lookup die abgeschlossene Aktivität in der Auflistung von Aktivitäten.
      • Erhöhen Sie der Index des letzten Elements ausgeführt.
      • Wenn der Index noch innerhalb der Grenzen der Children-Auflistung ist, Planen Sie das nächste untergeordnete Element.
      • Wiederholen.
  • Wenn Sie die GoTo-Textmarke fortgesetzt wird:
    • Ruft den Namen der Aktivität, der wir wechseln möchten.
    • Finden Sie diese Aktivität in der Auflistung Aktivitäten.
    • Planen Sie die Zielaktivität der Gruppe für die Ausführung, und registrieren Sie eine Abschlussrückruf, die die nächste Aktivität geplant wird.
    • Brechen Sie die Aktivität, die gerade ausgeführt wird.
    • Speichern Sie die Aktivität, die derzeit in der Variablen “ aktuelle ” ausgeführt wird.

Abbildung 4 im Codebeispiel zeigt die Implementierung für eine Reihe-Aktivität, die genau wie beschrieben verhält.

Abbildung 4 der Series-Aktivität

public class Series : NativeActivity
{
  internal static readonly string GotoPropertyName = 
    "Microsoft.Samples.CustomControlFlow.Series.Goto";

  // Child activities and variables collections
  Collection<Activity> activities;
  Collection<Variable> variables;

  // Activity instance that is currently being executed
  Variable<ActivityInstance> current = new Variable<ActivityInstance>();
 
  // For externally initiated goto's; optional
  public InArgument<string> BookmarkName { get; set; }

  public Series() { }

  public Collection<Activity> Activities 
  { 
    get {
      if (this.activities == null)
        this.activities = new Collection<Activity>();
    
      return this.activities; 
    } 
  }

  public Collection<Variable> Variables 
  { 
    get {
      if (this.variables == null)
        this.variables = new Collection<Variable>();

      return this.variables; 
    } 
  }
    
  protected override void CacheMetadata(NativeActivityMetadata metadata)
  {                        
    metadata.SetVariablesCollection(this.Variables);
    metadata.SetChildrenCollection(this.Activities);
    metadata.AddImplementationVariable(this.current);
    metadata.AddArgument(new RuntimeArgument("BookmarkName", typeof(string), 
                                              ArgumentDirection.In));
  }

  protected override bool CanInduceIdle { get { return true; } }

  protected override void Execute(NativeActivityContext context)
  {
    // If there activities in the collection...
if (this.Activities.Count > 0)
    {
      // Create a bookmark for signaling the GoTo
      Bookmark internalBookmark = context.CreateBookmark(this.Goto,
                BookmarkOptions.MultipleResume | BookmarkOptions.NonBlocking);

      // Save the name of the bookmark as an execution property
      context.Properties.Add(GotoPropertyName, internalBookmark);

      // Schedule the first item in the list and save the resulting 
      // ActivityInstance in the "current" implementation variable
      this.current.Set(context, context.ScheduleActivity(this.Activities[0], 
                                this.OnChildCompleted));

      // Create a bookmark for external (host) resumption
      if (this.BookmarkName.Get(context) != null)
        context.CreateBookmark(this.BookmarkName.Get(context), this.Goto,
            BookmarkOptions.MultipleResume | BookmarkOptions.NonBlocking);
    }
  }

  void Goto(NativeActivityContext context, Bookmark b, object obj)
  {
    // Get the name of the activity to go to
    string targetActivityName = obj as string;

    // Find the activity to go to in the children list
    Activity targetActivity = this.Activities
                                  .Where<Activity>(a =>  
                                         a.DisplayName.Equals(targetActivityName))
                                  .Single();

    // Schedule the activity 
    ActivityInstance instance = context.ScheduleActivity(targetActivity, 
                                                         this.OnChildCompleted);

    // Cancel the activity that is currently executing
    context.CancelChild(this.current.Get(context));

    // Set the activity that is executing now as the current
    this.current.Set(context, instance);
  }

  void OnChildCompleted(NativeActivityContext context, ActivityInstance completed)
  {
    // This callback also executes when cancelled child activities complete 
    if (completed.State == ActivityInstanceState.Closed)
    {
      // Find the next activity and execute it
      int completedActivityIndex = this.Activities.IndexOf(completed.Activity);
      int next = completedActivityIndex + 1;

      if (next < this.Activities.Count)
          this.current.Set(context, 
                           context.ScheduleActivity(this.Activities[next],
                           this.OnChildCompleted));
    }
  }
}

Einige der in diesem Code wird aus den vorherigen Beispielen vertraut. Erörtert die Implementierung dieser Aktivität.

Serie von NativeActivity abgeleitet wird, da für die Interaktion mit der WF-Laufzeit untergeordnete Aktivitäten planen, Lesezeichen erstellen, Kinder abbrechen und verwenden Sie die Eigenschaften für die Ausführung benötigt.

Ist als vor, der nächste Schritt für die Reihe die öffentliche Signatur definieren. Wie im SimpleSequence sind Aktivitäten und Variablen Auflistungseigenschaften. Es gibt auch ein Zeichenfolgenargument eingegebene Textmarkenname (vom Typ InArgument <string>) mit der Bezeichnung mit dem Namen der Textmarke für Host Wiederaufnahme erstellt werden. Wieder habe ich das Create Set-Einsatz Muster in den Aktivitätstyp folgende.

Serie verfügt über einen privaten Member mit der Bezeichnung “ aktuelle ”, die die ausgeführt wird, statt nur einen Zeiger auf ein Element in einer Auflistung, wie im SimpleSequence "ActivityInstance".. Warum ist der aktuelle einer Variablen <ActivityInstance>und keiner Variablen <int>? Weil ich Ahold des aktuell ausgeführten untergeordneten weiter unten in dieser Aktivität erhalten, während die GoTo-Methode. Die tatsächliche Details wird weiter unten erläutert;wichtig zu verstehen, ist jetzt, dass ich eine Variable Implementierung haben, der die Aktivitätsinstanz ausgeführt wird:

Variable<ActivityInstance> current = new Variable<ActivityInstance>();

CacheMetadata wird Common Language Runtime Informationen über Ihre Aktivität bereit: die untergeordneten Elemente und Variablen Auflistungen, die Implementierung-Variable mit der aktuellen Aktivitätsinstanz der und das Lesezeichen Name-Argument. Der einzige Unterschied besteht aus dem vorherigen Beispiel ist, dass ich das Eingabeargument Textmarkenname, innerhalb der WF-Laufzeit manuell registrieren bin – eine neue Instanz der RuntimeArgument auf die Aktivität Metadaten hinzufügen:

protected override void CacheMetadata(NativeActivityMetadata metadata)
{                        
  metadata.SetVariablesCollection(this.Variables);
  metadata.SetChildrenCollection(this.Activities);
  metadata.AddImplementationVariable(this.current);
  metadata.AddArgument(new RuntimeArgument("BookmarkName",  
                                           typeof(string), ArgumentDirection.In));
}

Als nächste neue ist die Eigenschaft CanInduceIdle-Überladung. Dies ist nur mehrere Metadaten, die die Aktivität der WF-Laufzeit. Wenn diese Eigenschaft true zurückgibt, bin ich mitteilen, der Common Language Runtime, dass diese Aktivität den Workflow in den Leerlauf verursachen kann. Muss ich diese Eigenschaft überschreiben und für Aktivitäten, die Lesezeichen, true zurückgeben, wie Sie den Workflow im Leerlauf, warten, bis die Wiederaufnahme gehen durchgeführt werden. Der Standardwert für diese Eigenschaft ist false. Diese Eigenschaft gibt false zurück, und wir eine Textmarke erstellen, werde ich eine InvalidOperationException-Ausnahme ist bei der Ausführung der Aktivität:

protected override bool CanInduceIdle { get { return true; } }

Dinge werden in der Execute-Methode, wobei ich Erstellen einer Textmarke (InternalBookmark) und speichern Sie es in eine Eigenschaft Ausführung noch interessanter. Vor der weiteren wechseln, jedoch lassen Sie mich Lesezeichen und Ausführung Eigenschaften eingeführt.

Lesezeichen handelt es sich um den Mechanismus, durch den eine Aktivität Passiv warten kann fortgesetzt werden. Wenn eine Aktivität “ ausstehende ein bestimmtes Ereignis blockieren ”, registriert eine Textmarke und gibt anschließend ein Ausführungsstatus des fortgesetzt werden kann. Dies signalisiert der Common Language Runtime, dass zwar nicht die Ausführung der Aktivität abgeschlossen ist, als Teil der aktuellen Arbeitsaufgabe beliebige stärker ausgelastet ist. Wenn Sie Lesezeichen verwenden, können Sie Ihre Aktivitäten mithilfe einer Maske der reaktive Ausführung erstellen: Wenn die Textmarke wird die Aktivität liefert erstellt, und wenn die Textmarke fortgesetzt wird ein Codeblock (Textmarke Wiederaufnahme Rückruf) in Reaktion auf die Textmarke Wiederaufnahme aufgerufen.

Im Gegensatz zu Programmen, die direkt auf die CLR sind die Workflowprogramme hierarchisch Bereichsbezogene Strukturen, die in einem Thread agnostischen-Umgebung ausgeführt werden. Dies impliziert, dass die standard-Thread lokalen Threadspeichers (TLS) Mechanismen direkt genutzt werden können nicht, um zu bestimmen, welchem Kontext im Gültigkeitsbereich für ein bestimmtes Arbeitselement. Der Ausführungskontext Workflow stellt Ausführung Eigenschaften-Umgebung mit einer Aktivität so, dass eine Aktivität Eigenschaften deklariert werden kann, die im Gültigkeitsbereich für seine untergeordneten Struktur und seine untergeordneten Elemente freigeben. Daher kann eine Aktivität Daten seiner nachfolgenden Werte mit diesen Eigenschaften bereitstellen.

Nun, da Sie Lesezeichen und Ausführung Eigenschaften kennen, let’s erhalten Sie zurück, für den Code. Was ich am Anfang der Execute-Methode gemacht habe eine Textmarke (mit context.CreateBookmark) erstellt und in eine Ausführung-Eigenschaft (mit context.Properties.Add) zu speichern. Diese Textmarke handelt es sich um ein Vielfaches Lebenslauf Lesezeichen, d. h. es kann mehrere Male fortgesetzt werden und verfügbar sein, während seine übergeordnete Aktivität ausgeführt wird. Es ist auch NonBlocking, damit es nicht wird nicht die Aktivität wird abgeschlossen, sobald es seine Aufgabe fertig ist. Wenn die Textmarke fortgesetzt wird, wird die GoTo-Methode aufgerufen werden, da ich einen BookmarkCompletionCallback CreateBookmark (den ersten Parameter) bereitgestellt. Der Grund für das Speichern in einer Ausführung-Eigenschaft besteht darin, Sie alle untergeordneten Aktivitäten zur Verfügung stellen. (Sie sehen später wie die GoTo-Aktivität dieses Lesezeichen verwendet.) Beachten Sie, dass die Ausführung Eigenschaften Namen haben. Da Name, eine Zeichenfolge wird definiert mit dem Namen der Eigenschaft in der Aktivität eine Konstante (GotoPropertyName). Dieser Name folgt einen Ansatz voll qualifizierten Namen. Dies ist eine bewährte Methode:

internal static readonly string GotoPropertyName = 
                                "Microsoft.Samples.CustomControlFlow.Series.Goto";

...
...
// Create a bookmark for signaling the GoTo
Bookmark internalBookmark = context.CreateBookmark(this.Goto,                                         
                       BookmarkOptions.MultipleResume | BookmarkOptions.NonBlocking);

// Save the name of the bookmark as an execution property
context.Properties.Add(GotoPropertyName, internalBookmark);

Nachdem ich die Textmarke deklariert haben, bin ich bereit, um meine erste Aktivität zu planen. Ich bin bereits kennen, da ich es in Meine vorherigen Aktivitäten hat. Ich werde die erste Aktivität in der Auflistung planen, und teilen Sie die Common Language Runtime die OnChildCompleted-Methode aufgerufen, wenn die Aktivität ausgeführt wird (wie in SimpleSequence). Context.ScheduleActivity gibt eine "ActivityInstance"., die eine Instanz einer Aktivität, die gerade ausgeführt wird, stellt die ich unsere aktuelle Implementierung Variable zuweisen. Lassen Sie mich dies verdeutlichen ein wenig. Die Aktivität ist die Definition, wie eine Klasse;"ActivityInstance". wird die tatsächliche Instanz, wie ein Objekt. Wir können mehrere ActivityInstances aus derselben Aktivität haben:

// Schedule the first item in the list and save the resulting 
// ActivityInstance in the "current" implementation variable
this.current.Set(context, context.ScheduleActivity(this.Activities[0],  
                                                   this.OnChildCompleted));

Abschließend erstellen Sie eine Textmarke, die vom Host verwendet werden, um für jede Aktivität in der Reihe zu springen. Die Mechanismen dafür ist einfach: Da der Host den Namen des Lesezeichens kennt, kann er mit einer Sprungmarke zu jeder Aktivität innerhalb der Reihe wieder aufgenommen:

// Create a bookmark for external (host) resumption
 if (this.BookmarkName.Get(context) != null)
     context.CreateBookmark(this.BookmarkName.Get(context), this.Goto,
                           BookmarkOptions.MultipleResume | BookmarkOptions.NonBlocking);

Die OnChildCompleted-Methode sollte nun einfach sein, wie es in SimpleSequence sehr ähnlich ist: Das nächste Element in der Auflistung Aktivitäten suchen, und Planen ihn. Der Hauptunterschied besteht darin, dass ich nur die nächste Aktivität, Planen Wenn die aktuelle Aktivität die Ausführung erfolgreich abgeschlossen (, die, im geschlossenen Zustand erreicht und noch nicht wurde abgebrochen oder ein Fehler aufgetreten).

Die GoTo-Methode ist wohl am interessantesten. Dies ist die Methode, die als Ergebnis des Lesezeichens GoTo fortgesetzt wird ausgeführt wird. Er empfängt Daten als Eingabe übergeben wird, wenn die Textmarke fortgesetzt wird. In diesem Fall sind die Daten, den Namen der Aktivität, der wir wechseln möchten:

void Goto(NativeActivityContext context, Bookmark b, object data)
{
  // Get the name of the activity to go to
  string targetActivityName = data as string;
       
  ...
}

Der Zielname-Aktivität ist die DisplayName-Eigenschaft der Aktivität. Ich Nachschlagen die angeforderte Aktivitätsdefinition in der Auflistung “ Aktivitäten ”. Nachdem ich die angeforderte Aktivität gefunden, Planen I, zurück, der angibt, dass die Methode OnChildCompleted nach Abschluss die Aktivität ausgeführt werden soll, wenn sollten:

// Find the activity to go to in the children list
Activity targetActivity = this.Activities
                              .Where<Activity>(a =>  
                                       a.DisplayName.Equals(targetActivityName))
                              .Single();
// Schedule the activity 
ActivityInstance instance = context.ScheduleActivity(targetActivity, 
                                                     this.OnChildCompleted);

Als Nächstes Abbrechen ich den Aktivitätsinstanz, der derzeit ausgeführt wird und die aktuelle Aktivität wird ausgeführt, um die "ActivityInstance". im vorherigen Schritt geplant. Für diese beiden Vorgänge verwende ich die “ aktuelle ” Variable. Erstens ich es als Parameter der Methode CancelChild des NativeActivityContext übergeben und dann deren Wert mit "ActivityInstance". angezeigt, die in den vorherigen Codeblock geplant aktualisieren:

// Cancel the activity that is currently executing
context.CancelChild(this.current.Get(context));

// Set the activity that is executing now as the current
this.current.Set(context, instance);

Die GoTo-Aktivität

Die GoTo-Aktivität kann nur innerhalb der Series-Aktivität So wechseln Sie zu einer Aktivität in der Auflistung von Aktivitäten verwendet werden. It’s similar to a GoTo statement in an imperative program. Das Verfahren ist sehr einfach: Es wird wieder aufgenommen, GoTo Textmarke erstellt, die von der Series-Aktivität, in der es enthalten ist, die den Namen der Aktivität, der wir wechseln möchten, haben. Wenn die Textmarke fortgesetzt wird, springt die Serie der Aktivität angezeigt.

Hier ist eine einfache Beschreibung die Semantik der Ausführung:

  • Der Benutzer Aktivität muss eine Zeichenfolge TargetActivityName bereitstellen. Dieses Argument ist erforderlich.
  • Zur Ausführungszeit:
    • Die GoTo-Aktivität wird erstellte, indem der Series-Aktivität “ GoTo ” Textmarke suchen.
    • Wenn die Textmarke gefunden wird, wird er es übergibt die TargetActivityName fortgesetzt.
    • Es wird eine Textmarke Synchronisierung erstellt, damit die Aktivität nicht abgeschlossen wird.
      • Es wird durch die Serie abgebrochen.

Der Code in Abbildung 5 zeigt die Implementierung für eine GoTo-Aktivität, die genau wie beschrieben verhält.

Abbildung 5 der GoTo-Aktivität

public class GoTo : NativeActivity
{
  public GoTo() 
  { }
       
  [RequiredArgument]
  public InArgument<string> TargetActivityName { get; set; }

  protected override bool CanInduceIdle { get { return true; } }
    
  protected override void Execute(NativeActivityContext context)
  {
    // Get the bookmark created by the parent Series
    Bookmark bookmark = context.Properties.Find(Series.GotoPropertyName) as Bookmark;

    // Resume the bookmark passing the target activity name
    context.ResumeBookmark(bookmark, this.TargetActivityName.Get(context));

    // Create a bookmark to leave this activity idle waiting when it does
    // not have any further work to do.
Series will cancel this activity 
    // in its GoTo method
    context.CreateBookmark("SyncBookmark");
  }

}

Gehe zu, die von NativeActivity abgeleitet werden, da für die Interaktion mit der WF-Laufzeit erstellen und fortsetzen Lesezeichen, und verwenden Sie Eigenschaften für die Ausführung benötigt. Seine öffentliche Signatur umfasst das Eingabeargument TargetActivityName Zeichenfolge, das den Namen der Aktivität enthält, die wir zu springen. Ich ergänzt das Argument mit dem Attribut RequiredArgument, was bedeutet, dass die Validierung WF-Dienste erzwungen werden, dass Sie mit einem Ausdruck festgelegt ist.

Ich basieren auf der Standardimplementierung der CacheMetadata, die auf der Oberfläche des öffentlichen der Aktivität zu finden, und registrieren die Metadaten für die Common Language Runtime entspricht.

Der wichtigste Teil ist in der Execute-Methode. Ich suchen Sie zuerst die Textmarke, die durch den übergeordneten Serien-Aktivität erstellt. Da die Textmarke als Eigenschaft Ausführung gespeichert wurde, suchen Sie ich dafür im context.Properties. Nachdem ich dieses Lesezeichen gefunden haben, wieder ich, die TargetActivityName als Eingabedaten übergeben. Diese Textmarke Wiederaufnahme führt die Series.Goto-Methode aufgerufen wird (weil es der Textmarke Rückruf bereitgestellt ist, wenn die Textmarke erstellt wurde). Dieser Methode suchen Sie nach der nächsten Aktivität in der Auflistung, Planen und Abbrechen die Aktivität, die gerade ausgeführt wird:

// Get the bookmark created by the parent Series
Bookmark bookmark = context.Properties.Find(Series.GotoPropertyName) as Bookmark; 

// Resume the bookmark passing the target activity name
context.ResumeBookmark(bookmark, this.TargetActivityName.Get(context));

Die letzte Zeile des Codes ist der schwierigste: Erstellen Sie eine Textmarke für die Synchronisierung, bleiben die GoTo-Aktivität ausgeführt. Folglich GoTo.Execute-Methode abgeschlossen ist, diese Aktivität noch werden Ausführen von Status, warten auf eine Reaktion die Textmarke fortsetzen. Erörtert den Code für die Series.Goto erwähnt, dass es die ausgeführte Aktivität abgebrochen. In diesem Fall ist Series.Goto tatsächlich eine Aktivitätsinstanz Gehe zu stornieren, die für dieses Lesezeichen fortgesetzt werden.

Ausführlicher erläutert: Die Instanz der GoTo-Aktivität wurde von der Series-Aktivität geplant. Nach Abschluss dieser Aktivität werden die Abschlussrückruf in der Reihe (OnChildCompleted) sucht nach der nächsten Aktivität in Series.Activities Auflistung und plant, es. In diesem Fall soll die nächste Aktivität planen möchten – die Aktivität auf die TargetActivityName planen möchten. Dieses Lesezeichen ermöglicht dies, da speichert die GoTo-Aktivität in einem ausführenden Status während die Zielaktivität berechnet wird. Wenn die GoTo abgebrochen wird, ist keine Aktion im Rückruf Series.OnChildCompleted, da es nur die nächste Aktivität, geplant Wenn der Abschlussstatus ist geschlossen (und in diesem Fall abgebrochen wird):

// Create a bookmark to leave this activity idle waiting when it does
// not have any further work to do.
Series will cancel this activity 
// in its GoTo method
context.CreateBookmark("SyncBookmark");

Abbildung 6 zeigt ein Beispiel für diese Aktivität verwenden. In diesem Fall habe ich wieder in einen früheren Zustand, den Wert einer Variablen Schleifen. Dies ist ein einfaches Beispiel zur Veranschaulichung der grundlegenden Verwendung der Serie, aber diese Aktivität kann verwendet werden, komplexe und realistischen Geschäftsszenarios implementieren, müssen Sie übersprungen, wiederholen oder um Schritte in einem sequenziellen Prozess springen.

Abbildung 6 mit GoTo in einer Reihe

var counter = new Variable<int>();

var act = new Series
{
  Variables = { counter},
  Activities =
  {
    new WriteLine 
    {
      DisplayName = "Start",
      Text = "Step 1"
    },
    new WriteLine
    {
      DisplayName = "First Step",
      Text = "Step 2"
    },
    new Assign<int>
    {
      To = counter,
      Value = new InArgument<int>(c => counter.Get(c) + 1)
    },
    new If 
    {
      Condition = new InArgument<bool>(c => counter.Get(c) == 3),
      Then = new WriteLine
      {
        Text = "Step 3"
      },
      Else = new GoTo { TargetActivityName = "First Step" }
    },
    new WriteLine 
    {
      Text = "The end!"
    }
  }
};

WorkflowInvoker.Invoke(act);

Verweise

Windows Workflow Foundation 4-Entwicklercenter
MSDN.Microsoft.com/netframework/aa663328

Endpoint.TV: Aktivitäten, die bewährte Methoden erstellen
channel9.msdn.com/Shows/Endpoint/endpointtv-Workflow-and-Custom-Activities-Best-Practices-Part-1/

Entwerfen und Implementieren von benutzerdefinierten Aktivitäten
MSDN.Microsoft.com/library/dd489425

Klasse "ActivityInstance".
MSDN.Microsoft.com/library/System.activities.ActivityInstance

RuntimeArgument-Klasse
MSDN.Microsoft.com/library/dd454495

Fahren Sie mit der Bewegung

In diesem Artikel vorgestellten ich die allgemeine Aspekte des benutzerdefinierten Steuerelements Fluss Aktivitäten schreiben. WF-4 wird das Steuerelement Fluss Spektrum nicht behoben;Schreiben von benutzerdefinierte Aktivitäten wurde erheblich vereinfacht. Wenn die Aktivitäten bereitgestellt Out-of-the-Box nicht Ihren Anforderungen entsprechen, können Sie problemlos Ihre eigene erstellen. In diesem Artikel ich ein einfaches Steuerelement Datenfluss-Aktivität gestartet und dann arbeitete meine Art bis zum Implementieren eines benutzerdefinierten Steuerelements Fluss Aktivität hinzugefügt, die neue Ausführung Semantik WF-4. Wenn Sie mehr erfahren möchten, ist eine Community Technology Preview für State Machine unter CodePlex mit vollständigem Quellcode verfügbar. Außerdem finden Sie eine Reihe von Channel 9-Videos auf Aktivität Erstellen von optimale Methoden. Durch das Schreiben eigener benutzerdefinierten Aktivitäten, können alle Steuerelementmuster-Fluss in WF auszudrücken und WF ausreicht, um die Besonderheiten des Problems.

Leon Welicki is a program manager in the Windows Workflow Foundation (WF) team at Microsoft working on the WF runtime. Vor dem zur Teilnahme an Microsoft arbeitete er als Architekt und Abw-Manager für ein Unternehmen mit großen Spanisch Telekommunikation und als eine externe zuordnen Professor für das Studium Computer Wissenschaft Fakultäten, in der Pontifical Universität von Salamanca in Madrid.

Dank an die folgenden technischen Experten für die Überprüfung der in diesem Artikel: Joe Clancy, Dan Glick, Rajesh Sampath, Bob Schmidt and Isaac Yuen