Regeln propagieren Änderungen im Modell

Sie können im SDK für Visualisierung und Modellierung (VMSDK) eine Speicherregel erstellen, um eine Änderung von einem Element an ein anderes weiterzugeben. Wenn eine Änderung an einem Element im Speicher auftritt, wird die Ausführung von Regeln geplant. Dies geschieht normalerweise, wenn die äußerste Transaktion committet wird. Es gibt verschiedene Arten von Regeln für verschiedene Arten von Ereignissen, z. B. das Hinzufügen oder Löschen eines Elements. Sie können Regeln an bestimmte Typen von Elementen, Formen oder Diagrammen anfügen. Viele integrierte Features werden durch Regeln definiert: Regeln stellen beispielsweise sicher, dass ein Diagramm aktualisiert wird, wenn sich das Modell ändert. Sie können Ihre domänenspezifische Sprache anpassen, indem Sie eigene Regeln hinzufügen.

Speicherregeln sind besonders nützlich für die Weitergabe von Änderungen innerhalb des Speichers, d. h. Änderungen an Modellelementen, Beziehungen, Formen oder Verbindern und deren Domäneneigenschaften. Regeln werden nicht ausgeführt, wenn der Benutzer die Befehle „Rückgängig“ oder „Wiederholen“ aufruft. Stattdessen stellt der Transaktions-Manager sicher, dass der Inhalt des Speichers im richtigen Zustand wiederhergestellt wird. Wenn Sie Änderungen an Ressourcen außerhalb des Speichers weitergeben möchten, verwenden Sie Speicherereignisse. Weitere Informationen finden Sie unter Ereignishandler geben Änderungen außerhalb des Modells weiter.

Angenommen, Sie möchten angeben, dass jedes Mal, wenn der Benutzer (oder Ihr Code) ein neues Element vom Typ ExampleDomainClass erstellt, ein zusätzliches Element eines anderen Typs in einem anderen Teil des Modells erstellt wird. Sie könnten eine AddRule schreiben und diese ExampleDomainClass zuordnen. Sie würden Code in die Regel schreiben, um das zusätzliche Element zu erstellen.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.Modeling;

namespace ExampleNamespace
{
 // Attribute associates the rule with a domain class:
 [RuleOn(typeof(ExampleDomainClass), FireTime=TimeToFire.TopLevelCommit)]
 // The rule is a class derived from one of the abstract rules:
 class MyAddRule : AddRule
 {
  // Override the abstract method:
  public override void ElementAdded(ElementAddedEventArgs e)
  {
    base.ElementAdded(e);
    ExampleDomainClass element = e.ModelElement;
    Store store = element.Store;
    // Ignore this call if we're currently loading a model:
    if (store.TransactionManager.CurrentTransaction.IsSerializing)
       return;

    // Code here propagates change as required - for example:
      AnotherDomainClass echo = new AnotherDomainClass(element.Partition);
      echo.Name = element.Name;
      echo.Parent = element.Parent;
    }
  }
 // The rule must be registered:
 public partial class ExampleDomainModel
 {
   protected override Type[] GetCustomDomainModelTypes()
   {
     List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
     types.Add(typeof(MyAddRule));
     // If you add more rules, list them here.
     return types.ToArray();
   }
 }
}

Hinweis

Der Code einer Regel sollte nur den Zustand von Elementen im Speicher ändern. Das heißt, die Regel sollte nur Modellelemente, Beziehungen, Formen, Verbinder, Diagramme oder deren Eigenschaften ändern. Wenn Sie Änderungen an Ressourcen außerhalb des Speichers weitergeben möchten, definieren Sie Speicherereignisse. Weitere Informationen finden Sie unter Ereignishandler geben Änderungen außerhalb des Modells weiter.

So definieren Sie eine Regel

  1. Definieren Sie die Regel als Klasse, der das RuleOn-Attribut als Präfix vorangestellt ist. Das Attribut ordnet die Regel einer Ihrer Domänenklassen, Beziehungen oder Diagrammelemente zu. Die Regel wird auf jede Instanz dieser Klasse angewendet, die abstrakt sein kann.

  2. Registrieren Sie die Regel, indem Sie sie der Menge hinzufügen, die von GetCustomDomainModelTypes() in Ihrer Domänenmodellklasse zurückgegeben wird.

  3. Leiten Sie die Regelklasse von einer der abstrakten Regelklassen ab, und schreiben Sie den Code der Ausführungsmethode.

    In den folgenden Abschnitten werden diese Schritte ausführlicher beschrieben.

So definieren Sie eine Regel für eine Domänenklasse

  • Definieren Sie in einer benutzerdefinierten Codedatei eine Klasse, und stellen Sie ihr das RuleOnAttribute-Attribut als Präfix voran:

    [RuleOn(typeof(ExampleElement),
         // Usual value - but required, because it is not the default:
         FireTime = TimeToFire.TopLevelCommit)]
    class MyRule ...
    
    
  • Der Antragstellertyp im ersten Parameter kann eine Domänenklasse, eine Domänenbeziehung, eine Form, ein Verbinder oder ein Diagramm sein. In der Regel wenden Sie Regeln auf Domänenklassen und Beziehungen an.

    FireTime ist normalerweise TopLevelCommit. Dadurch wird sichergestellt, dass die Regel erst ausgeführt wird, nachdem alle primären Änderungen der Transaktion vorgenommen wurden. Die Alternativen sind inline, wodurch die Regel kurz nach der Änderung ausgeführt wird, und LocalCommit, wodurch die Regel am Ende der aktuellen Transaktion ausgeführt wird (die möglicherweise nicht die äußerste Transaktion ist). Sie können auch die Priorität einer Regel festlegen, um die Reihenfolge in der Warteschlange zu beeinflussen. Dies ist jedoch eine unzuverlässige Methode, um das erforderliche Ergebnis zu erzielen.

  • Sie können eine abstrakte Klasse als Antragstellertyp angeben.

  • Die Regel gilt für alle Instanzen der Antragstellerklasse.

  • Der Standardwert für FireTime ist TimeToFire.TopLevelCommit. Dies bewirkt, dass die Regel ausgeführt wird, wenn die äußerste Transaktion committet wird. Eine Alternative ist TimeToFire.Inline. Dies bewirkt, dass die Regel kurz nach dem auslösenden Ereignis ausgeführt wird.

So registrieren Sie die Regel

  • Fügen Sie die Regelklasse der Liste der Typen hinzu, die von GetCustomDomainModelTypes in Ihrem Domänenmodell zurückgegeben werden:

    public partial class ExampleDomainModel
     {
       protected override Type[] GetCustomDomainModelTypes()
       {
         List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
         types.Add(typeof(MyAddRule));
         // If you add more rules, list them here.
         return types.ToArray();
       }
     }
    
    
  • Wenn Sie sich des Namens Ihrer Domänenmodellklasse nicht sicher sind, suchen Sie in der Datei Dsl\GeneratedCode\DomainModel.cs nach ihm.

  • Schreiben Sie diesen Code in einer benutzerdefinierten Codedatei in Ihrem DSL-Projekt.

So schreiben Sie den Code der Regel

  • Leiten Sie die Regelklasse von einer der folgenden Basisklassen ab:

    Basisklasse Trigger
    AddRule Ein Element, eine Verknüpfung oder eine Form wird hinzugefügt.

    Verwenden Sie diese Option, um neue Beziehungen zusätzlich zu neuen Elementen zu erkennen.
    ChangeRule Ein Domäneneigenschaftswert wird geändert. Das Methodenargument stellt die alten und die neuen Werte bereit.

    Bei Formen wird diese Regel ausgelöst, wenn sich die integrierte AbsoluteBounds-Eigenschaft ändert, wenn die Form verschoben wird.

    In vielen Fällen ist es bequemer, OnValueChanged oder OnValueChanging im Eigenschaftenhandler zu überschreiben. Diese Methoden werden unmittelbar vor und nach der Änderung aufgerufen. Im Gegensatz dazu wird die Regel in der Regel am Ende der Transaktion ausgeführt. Weitere Informationen finden Sie unter Änderungshandler für Domäneneigenschaftenwert. Hinweis: Diese Regel wird nicht ausgelöst, wenn eine Verknüpfung erstellt oder gelöscht wird. Schreiben Sie stattdessen eine AddRule und eine DeleteRule für die Domänenbeziehung.
    DeletingRule Wird ausgelöst, wenn ein Element oder eine Verknüpfung gelöscht werden soll. Die Eigenschaft ModelElement.IsDeleting ist bis zum Ende der Transaktion TRUE.
    DeleteRule Wird ausgeführt, wenn ein Element oder eine Verknüpfung gelöscht wurde. Die Regel wird ausgeführt, nachdem alle anderen Regeln ausgeführt wurden, einschließlich DeletingRules. ModelElement.IsDeleting ist FALSE und ModelElement.IsDeleted ist TRUE. Um einen nachfolgenden Rückgängig-Vorgang zu ermöglichen, wird das Element nicht tatsächlich aus dem Speicher entfernt, sondern nur aus Store.ElementDirectory.
    MoveRule Ein Element wird von einer Speicherpartition in eine andere verschoben.

    (Beachten Sie, dass dies nicht mit der grafischen Position einer Form zusammenhängt.)
    RolePlayerChangeRule Diese Regel gilt nur für Domänenbeziehungen. Sie wird ausgelöst, wenn Sie einer der beiden Seiten einer Verknüpfung explizit ein Modellelement zuweisen.
    RolePlayerPositionChangeRule Wird ausgelöst, wenn die Reihenfolge von Verknüpfungen mit oder aus einem Element mithilfe der MoveBefore- oder MoveToIndex-Methoden für eine Verknüpfung geändert wird.
    TransactionBeginningRule Wird ausgeführt, wenn eine Transaktion erstellt wird.
    TransactionCommittingRule Wird ausgeführt, wenn für die Transaktion ein Commit unmittelbar bevorsteht.
    TransactionRollingBackRule Wird ausgeführt, wenn für die Transaktion ein Rollback unmittelbar bevorsteht.
  • Jede Klasse verfügt über eine Methode, die Sie überschreiben. Geben Sie override Ihre Klasse ein, um sie zu ermitteln. Der Parameter dieser Methode identifiziert das Element, das geändert wird.

    Beachten Sie die folgenden Punkte zu Regeln:

  1. Der Satz von Änderungen in einer Transaktion kann zahlreiche Regeln auslösen. Normalerweise werden die Regeln ausgeführt, wenn die äußerste Transaktion committet wird. Sie werden in einer nicht angegebenen Reihenfolge ausgeführt.

  2. Eine Regel wird immer innerhalb einer Transaktion ausgeführt. Daher müssen Sie keine neue Transaktion erstellen, um Änderungen vorzunehmen.

  3. Regeln werden nicht ausgeführt, wenn für eine Transaktion ein Rollback ausgeführt wird oder wenn die Rückgängig- oder Wiederholungsvorgänge ausgeführt werden. Durch diese Vorgänge wird der gesamte Inhalt des Speichers auf den vorherigen Zustand zurückgesetzt. Wenn Ihre Regel den Zustand von Objekten außerhalb des Speichers ändert, wird sie daher möglicherweise nicht mit dem Speicherinhalt synchronisiert. Um den Zustand außerhalb des Speichers zu aktualisieren, ist es besser, Ereignisse zu verwenden. Weitere Informationen finden Sie unter Ereignishandler geben Änderungen außerhalb des Modells weiter.

  4. Einige Regeln werden ausgeführt, wenn ein Modell aus einer Datei geladen wird. Verwenden Sie store.TransactionManager.CurrentTransaction.IsSerializing, um zu bestimmen, ob aktuell ein Lade- oder Speichervorgang ausgeführt wird.

  5. Wenn der Code Ihrer Regel weitere Regeltrigger erstellt, werden diese am Ende der Auslöseliste hinzugefügt und ausgeführt, bevor die Transaktion abgeschlossen ist. DeletedRules werden nach allen anderen Regeln ausgeführt. Eine Regel kann in einer Transaktion mehrmals ausgeführt werden, ein Mal für jede Änderung.

  6. Um Informationen an und aus Regeln zu übergeben, können Sie Informationen im TransactionContext speichern. Dies ist nur ein Wörterbuch, das während der Transaktion verwaltet wird. Es wird verworfen, wenn die Transaktion endet. Die Ereignisargumente in jeder Regel bieten Zugriff darauf. Denken Sie daran, dass Regeln nicht in einer vorhersagbaren Reihenfolge ausgeführt werden.

  7. Verwenden Sie Regeln, nachdem Sie andere Alternativen in Betracht gezogen haben. Wenn Sie beispielsweise eine Eigenschaft aktualisieren möchten, wenn sich ein Wert ändert, sollten Sie eine berechnete Eigenschaft verwenden. Wenn Sie die Größe oder Position einer Form einschränken möchten, verwenden Sie eine BoundsRule. Wenn Sie auf eine Änderung eines Eigenschaftswerts reagieren möchten, fügen Sie der Eigenschaft einen OnValueChanged-Handler hinzu. Weitere Informationen finden Sie unter Reagieren auf und Weitergeben von Änderungen.

Beispiel

Im folgenden Beispiel wird eine Eigenschaft aktualisiert, wenn eine Domänenbeziehung instanziiert wird, um zwei Elemente zu verknüpfen. Die Regel wird nicht nur ausgelöst, wenn der Benutzer eine Verknüpfung für ein Diagramm erstellt, sondern auch, wenn der Programmcode eine Verknüpfung erstellt.

Um dieses Beispiel zu testen, erstellen Sie eine DSL mithilfe der Projektmappenvorlage „Task Flow“ (Aufgabenablauf), und fügen Sie den folgenden Code in eine Datei im Dsl-Projekt ein. Erstellen Sie die Projektmappe, führen Sie sie aus, und öffnen Sie die Beispieldatei im Debugprojekt. Zeichnen Sie eine Kommentarverknüpfung zwischen einer Comment-Form und einem Flow-Element. Der Text im Kommentar ändert sich so, dass er über das neueste Element berichtet, mit dem Sie ihn verbunden haben.

In der Praxis schreiben Sie in der Regel eine DeleteRule für jede AddRule.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Modeling;

namespace Company.TaskRuleExample
{

  [RuleOn(typeof(CommentReferencesSubjects))]
  public class RoleRule : AddRule
  {

    public override void ElementAdded(ElementAddedEventArgs e)
    {
      base.ElementAdded(e);
      CommentReferencesSubjects link = e.ModelElement as CommentReferencesSubjects;
      Comment comment = link.Comment;
      FlowElement subject = link.Subject;
      Transaction current = link.Store.TransactionManager.CurrentTransaction;
      // Don't want to run when we're just loading from file:
      if (current.IsSerializing) return;
      comment.Text = "Flow has " + subject.FlowTo.Count + " outgoing connections";
    }

  }

  public partial class TaskRuleExampleDomainModel
  {
    protected override Type[] GetCustomDomainModelTypes()
    {
      List<Type> types = new List<Type>(base.GetCustomDomainModelTypes());
      types.Add(typeof(RoleRule));
      return types.ToArray();
    }
  }

}