Pravidla šířící změny v modelu

V sadě Visual And Modeling SDK (VMSDK) můžete vytvořit pravidlo úložiště, které rozšíří změnu z jednoho prvku do druhého. Když dojde ke změně libovolného prvku v úložišti, pravidla jsou naplánována ke spuštění, obvykle při potvrzení vnější transakce. Existují různé typy pravidel pro různé druhy událostí, například přidání prvku nebo jeho odstranění. Pravidla můžete připojit ke konkrétním typům prvků, obrazců nebo diagramů. Mnoho předdefinovaných funkcí je definováno pravidly: například pravidla zajišťují aktualizaci diagramu při změně modelu. Jazyk specifický pro doménu můžete přizpůsobit přidáním vlastních pravidel.

Pravidla úložiště jsou obzvláště užitečná pro šíření změn v úložišti – to znamená změny prvků modelu, relací, obrazců nebo spojnic a jejich vlastností domény. Pravidla se nespustí, když uživatel vyvolá příkazy Zpět nebo Znovu. Místo toho správce transakcí zajistí, že se obsah úložiště obnoví do správného stavu. Pokud chcete rozšířit změny do prostředků mimo úložiště, použijte události úložiště. Další informace naleznete v tématu Obslužné rutiny událostí šíření změn mimo model.

Předpokládejme například, že chcete určit, že pokaždé, když uživatel (nebo váš kód) vytvoří nový prvek typu ExampleDomainClass, další prvek jiného typu se vytvoří v jiné části modelu. Můžete napsat addRule a přidružit ho k ExampleDomainClass. V pravidle byste napsali kód pro vytvoření dalšího prvku.

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

Poznámka:

Kód pravidla by měl změnit stav pouze prvků v úložišti; to znamená, že pravidlo by mělo změnit pouze prvky modelu, relace, obrazce, spojnice, diagramy nebo jejich vlastnosti. Pokud chcete rozšířit změny do prostředků mimo úložiště, definujte události úložiště. Další informace naleznete v tématu Obslužné rutiny událostí šíření změn mimo model.

Definování pravidla

  1. Definujte pravidlo jako třídu s předponou atributu RuleOn . Atribut přidruží pravidlo k jedné z vašich tříd domény, relací nebo prvků diagramu. Pravidlo se použije pro každou instanci této třídy, která může být abstraktní.

  2. Zaregistrujte pravidlo tak, že ho přidáte do sady vrácené GetCustomDomainModelTypes() ve vaší třídě doménového modelu.

  3. Odvozujte třídu pravidla z jedné z abstraktní třídy Rule a napište kód metody provádění.

    Následující části popisují tyto kroky podrobněji.

Definování pravidla pro třídu domény

  • V souboru vlastního kódu definujte třídu a předponu atributem RuleOnAttribute :

    [RuleOn(typeof(ExampleElement),
         // Usual value - but required, because it is not the default:
         FireTime = TimeToFire.TopLevelCommit)]
    class MyRule ...
    
    
  • Typ předmětu v prvním parametru může být doménová třída, vztah domény, obrazec, spojnice nebo diagram. Obvykle platí pravidla pro třídy domény a relace.

    Obvykle je TopLevelCommitto FireTime . Tím se zajistí, že se pravidlo spustí až po provedení všech primárních změn transakce. Alternativy jsou vložené, což spustí pravidlo brzy po změně; a LocalCommit, který provádí pravidlo na konci aktuální transakce (což nemusí být vnější). Můžete také nastavit prioritu pravidla tak, aby ovlivnila její řazení ve frontě, ale jedná se o nespolehlivý způsob dosažení požadovaného výsledku.

  • Abstraktní třídu můžete zadat jako typ předmětu.

  • Pravidlo platí pro všechny instance třídy předmětu.

  • Výchozí hodnota FireTime je TimeToFire.TopLevelCommit. To způsobí, že pravidlo se spustí při potvrzení vnější transakce. Alternativou je TimeToFire.Inline. To způsobí, že se pravidlo spustí brzy po aktivační události.

Registrace pravidla

  • Přidejte třídu pravidla do seznamu typů vrácených ve vašem doménovém GetCustomDomainModelTypes modelu:

    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();
       }
     }
    
    
  • Pokud si nejste jistí názvem vaší třídy doménového modelu, podívejte se do souboru Dsl\GeneratedCode\DomainModel.cs.

  • Tento kód napište do vlastního souboru kódu v projektu DSL.

Napsání kódu pravidla

  • Odvodit třídu pravidla z jedné z následujících základních tříd:

    Základní třída Trigger
    AddRule Přidá se prvek, propojení nebo obrazec.

    Tato možnost slouží k detekci nových relací kromě nových prvků.
    ChangeRule Hodnota vlastnosti domény se změní. Argument metody poskytuje staré a nové hodnoty.

    U obrazců se toto pravidlo aktivuje, když se předdefinovaná AbsoluteBounds vlastnost změní, pokud se obrazec přesune.

    V mnohapřípadechch OnValueChangedOnValueChanging Tyto metody se volají bezprostředně před a po změně. Naproti tomu pravidlo obvykle běží na konci transakce. Další informace naleznete v tématu Domain Property Value Change Handlers. Poznámka: Toto pravidlo se neaktivuje při vytvoření nebo odstranění odkazu. Místo toho napište vztah AddRule domény a jeho název DeleteRule .
    DeletingRule Aktivuje se, když se chystá odstranit prvek nebo odkaz. Vlastnost ModelElement.IsDeleting je true až do konce transakce.
    DeleteRule Provede se při odstranění prvku nebo propojení. Pravidlo se spustí po provedení všech ostatních pravidel, včetně odstranění pravidel. ModelElement.IsDeleting je false a ModelElement.IsDeleted je true. Chcete-li povolit následné vrácení zpět, prvek není ve skutečnosti odebrán z paměti, ale je odebrán z Store.ElementDirectory.
    MoveRule Prvek se přesune z jednoho oddílu úložiště do druhého.

    (Všimněte si, že to nesouvisí s grafickou polohou obrazce.)
    RolePlayerChangeRule Toto pravidlo platí jenom pro vztahy domén. Aktivuje se, pokud explicitně přiřadíte prvek modelu k některému konci propojení.
    RolePlayerPositionChangeRule Aktivováno při řazení odkazů na nebo z elementu je změněn pomocí MoveBefore nebo MoveToIndex metody na odkazu.
    TransactionBeginningRule Provede se při vytvoření transakce.
    TransactionCommittingRule Provede se, když se transakce bude potvrzena.
    TransactionRollingBackRule Provede se, když se transakce chystá vrátit zpět.
  • Každá třída má metodu, kterou přepíšete. Zadejte override třídu, abyste ji objevili. Parametr této metody identifikuje prvek, který se mění.

    Všimněte si následujících bodů o pravidlech:

  1. Sada změn v transakci může aktivovat mnoho pravidel. Pravidla se obvykle provádějí při potvrzení nejkrajnější transakce. Jsou spouštěné v nezadaném pořadí.

  2. Pravidlo se vždy provádí uvnitř transakce. Proto nemusíte vytvářet novou transakci, aby bylo nutné provádět změny.

  3. Pravidla se nespouštějí při vrácení transakce zpět nebo při provádění operací Zpět nebo Znovu. Tyto operace resetují veškerý obsah Storu do předchozího stavu. Proto pokud pravidlo změní stav čehokoli mimo Store, nemusí být synchronizovaný s obsahem Storu. Pokud chcete aktualizovat stav mimo Store, je lepší použít události. Další informace naleznete v tématu Obslužné rutiny událostí šíření změn mimo model.

  4. Některá pravidla se spustí při načtení modelu ze souboru. Chcete-li zjistit, zda probíhá načítání nebo ukládání, použijte store.TransactionManager.CurrentTransaction.IsSerializing.

  5. Pokud kód pravidla vytvoří více aktivačních událostí pravidla, přidají se na konec seznamu aktivací a spustí se před dokončením transakce. Odstraněné pravidla se spustí po všech ostatních pravidlech. Jedno pravidlo může v transakci běžet mnohokrát, jednou pro každou změnu.

  6. Chcete-li předávat informace do pravidel a z pravidel, můžete ukládat informace do souboru TransactionContext. Jedná se pouze o slovník, který se udržuje během transakce. Je uvolněn při ukončení transakce. Argumenty události v jednotlivých pravidlech poskytují přístup. Mějte na paměti, že pravidla se nespouštějí předvídatelným pořadím.

  7. Po zvážení jiných alternativ používejte pravidla. Pokud například chcete aktualizovat vlastnost při změně hodnoty, zvažte použití počítané vlastnosti. Pokud chcete omezit velikost nebo umístění obrazce, použijte .BoundsRule Pokud chcete odpovědět na změnu hodnoty vlastnosti, přidejte do vlastnosti obslužnou OnValueChanged rutinu. Další informace naleznete v tématu Reakce na a šíření změn.

Příklad

Následující příklad aktualizuje vlastnost při vytvoření instance relace domény pro propojení dvou prvků. Pravidlo se aktivuje nejen v případě, že uživatel vytvoří odkaz v diagramu, ale také v případě, že kód programu vytvoří odkaz.

Pokud chcete tento příklad otestovat, vytvořte DSL pomocí šablony řešení Tok úloh a vložte následující kód do souboru v projektu Dsl. Sestavte a spusťte řešení a otevřete ukázkový soubor v projektu ladění. Nakreslete odkaz na komentář mezi obrazcem Komentáře a prvkem toku. Text v komentáři se změní na sestavu nejnovějšího prvku, ke kterému jste ho připojili.

V praxi byste obvykle napsali DeleteRule pro každou 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();
    }
  }

}