Regler sprider ändringar i modellen

Du kan skapa en butiksregel för att sprida en ändring från ett element till ett annat i Visualisering och modellerings-SDK (VMSDK). När en ändring sker för ett element i Store schemaläggs regler som ska köras, vanligtvis när den yttersta transaktionen har checkats in. Det finns olika typer av regler för olika typer av händelser, till exempel att lägga till ett element eller ta bort det. Du kan koppla regler till specifika typer av element, former eller diagram. Många inbyggda funktioner definieras av regler: till exempel ser regler till att ett diagram uppdateras när modellen ändras. Du kan anpassa ditt domänspecifika språk genom att lägga till egna regler.

Store-regler är särskilt användbara för att sprida ändringar i lagret, det vill säga: ändringar i modellelement, relationer, former eller kopplingar samt deras domänegenskaper. Regler körs inte när användaren anropar kommandona Ångra eller Gör om. Transaktionshanteraren ser i stället till att butiksinnehållet återställs till rätt tillstånd. Om du vill sprida ändringar till resurser utanför butiken använder du Store-händelser. Mer information finns i Händelsehanterare sprider ändringar utanför modellen.

Anta till exempel att du vill ange att när användaren (eller koden) skapar ett nytt element av typen ExampleDomainClass skapas ytterligare ett element av en annan typ i en annan del av modellen. Du kan skriva en AddRule och associera den med ExampleDomainClass. Du skulle skriva kod i regeln för att skapa ytterligare element.

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

Anmärkning

Koden för en regel bör endast ändra tillståndet för element i Store. Regeln bör alltså endast ändra modellelement, relationer, former, kopplingar, diagram eller deras egenskaper. Om du vill sprida ändringar till resurser utanför butiken definierar du butikshändelser. Mer information finns i Händelsehanterare sprider ändringar utanför modellen.

Definiera en regel

  1. Definiera regeln som ett klassprefix med RuleOn attributet . Attributet associerar regeln med ett av dina domänklasser, relationer eller diagramelement. Regeln tillämpas på varje instans av den här klassen, vilket kan vara abstrakt.

  2. Registrera regeln genom att lägga till den i den uppsättning som returneras av GetCustomDomainModelTypes() i domänmodellklassen.

  3. Härled regelklassen från en av de abstrakta regelklasserna och skriv koden för körningsmetoden.

    I följande avsnitt beskrivs de här stegen mer detaljerat.

Så här definierar du en regel för en domänklass

  • I en anpassad kodfil definierar du en klass och prefixet med attributet RuleOnAttribute :

    [RuleOn(typeof(ExampleElement),
         // Usual value - but required, because it is not the default:
         FireTime = TimeToFire.TopLevelCommit)]
    class MyRule ...
    
    
  • Ämnestypen i den första parametern kan vara en domänklass, domänrelation, form, koppling eller diagram. Vanligtvis tillämpar du regler på domänklasser och relationer.

    FireTime är vanligtvis TopLevelCommit. Detta säkerställer att regeln körs först när alla primära ändringar i transaktionen har gjorts. Alternativen är Inline, som kör regeln strax efter ändringen; och LocalCommit, som kör regeln i slutet av den aktuella transaktionen (som kanske inte är den yttersta). Du kan också ange prioriteten för en regel för att påverka dess ordning i kön, men det här är en opålitlig metod för att uppnå det resultat du behöver.

  • Du kan ange en abstrakt klass som ämnestyp.

  • Regeln gäller för alla instanser av ämnesklassen.

  • Standardvärdet för FireTime är TimeToFire.TopLevelCommit. Detta gör att regeln körs när den översta transaktionen bekräftas. Ett alternativ är TimeToFire.Inline. Detta gör att regeln körs strax efter utlösande händelsen.

Registrera regeln

  • Lägg till regelklassen i listan över typer som returneras av GetCustomDomainModelTypes i domänmodellen:

    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();
       }
     }
    
    
  • Om du inte är säker på namnet på domänmodellklassen tittar du i filen Dsl\GeneratedCode\DomainModel.cs

  • Skriv den här koden i en anpassad kodfil i DSL-projektet.

Så här skriver du regelns kod

  • Härled regelklassen från någon av följande basklasser:

    Basklass Trigger
    AddRule Ett element, en länk eller en form läggs till.

    Använd detta för att identifiera nya relationer, förutom nya element.
    ChangeRule Ett egenskapsvärde för domänen ändras. Metodargumentet innehåller de gamla och nya värdena.

    För former utlöses den här regeln när den inbyggda AbsoluteBounds egenskapen ändras, om formen flyttas.

    I många fall är det enklare att åsidosätta OnValueChanged eller OnValueChanging i egenskapshanteraren. Dessa metoder anropas omedelbart före och efter ändringen. Regeln körs däremot vanligtvis i slutet av transaktionen. Mer information finns i Ändringshanterare för domänegenskapsvärden. Not: Den här regeln utlöses inte när en länk skapas eller tas bort. Skriv i stället en AddRule och en DeleteRule för domänrelationen.
    DeletingRule Utlöses när ett element eller en länk är på väg att tas bort. Egenskapen ModelElement.IsDeleting är sann fram till slutet av transaktionen.
    DeleteRule Utförs när ett element eller en länk har tagits bort. Regeln körs när alla andra regler har körts, inklusive Ta bortRegler. ModelElement.IsDeleting är falskt och ModelElement.IsDeleted är sant. För att möjliggöra en senare Ångra-funktion tas elementet inte bort från minnet, men det tas bort från Store.ElementDirectory.
    MoveRule Ett element flyttas från en lagringspartition till en annan.

    (Observera att detta inte är relaterat till formens grafiska position.)
    RolePlayerChangeRule Den här regeln gäller endast för domänrelationer. Det utlöses om du uttryckligen tilldelar ett modellelement till någon av länkarnas ände.
    RolePlayerPositionChangeRule Utlöses när ordningen på länkar till eller från ett element ändras med metoderna MoveBefore eller MoveToIndex på en länk.
    TransactionBeginningRule Körs när en transaktion skapas.
    TransactionCommittingRule Körs när transaktionen är på väg att bekräftas.
    TransactionRollingBackRule Körs när transaktionen är på väg att rullas tillbaka.
  • Varje klass har en metod som du åsidosätter. Skriv override i din klass för att upptäcka den. Parametern för den här metoden identifierar det element som ändras.

    Observera följande punkter om regler:

  1. Uppsättningen med ändringar i en transaktion kan utlösa många regler. Vanligtvis körs reglerna när den yttersta transaktionen bekräftas. De körs i en ospecificerad ordning.

  2. En regel körs alltid i en transaktion. Därför behöver du inte skapa en ny transaktion för att göra ändringar.

  3. Regler körs inte när en transaktion återställs eller när åtgärderna Ångra eller Upprepa utförs. Dessa åtgärder återställer allt innehåll i Store till dess tidigare tillstånd. Om din regel ändrar tillståndet för något utanför Store kanske den inte synkroniseras med Store-innehållet. Om du vill uppdatera tillståndet utanför Store är det bättre att använda Händelser. Mer information finns i Händelsehanterare sprider ändringar utanför modellen.

  4. Vissa regler körs när en modell läses in från filen. Om du vill avgöra om inläsning eller sparande pågår använder du store.TransactionManager.CurrentTransaction.IsSerializing.

  5. Om koden för regeln skapar fler regelutlösare, läggs de till i slutet av utlösarlistan och de körs innan transaktionen slutförs. DeletedRules körs efter alla andra regler. En regel kan köras många gånger i en transaktion, en gång för varje ändring.

  6. Om du vill skicka information till och från regler kan du lagra information i TransactionContext. Det här är bara en ordlista som underhålls under transaktionen. Den tas bort när transaktionen avslutas. Händelseargumenten i varje regel ger åtkomst till den. Kom ihåg att regler inte körs i en förutsägbar ordning.

  7. Använd regler när du har övervägt andra alternativ. Om du till exempel vill uppdatera en egenskap när ett värde ändras bör du överväga att använda en beräknad egenskap. Om du vill begränsa storleken eller platsen för en form använder du en BoundsRule. Om du vill svara på en ändring i ett egenskapsvärde lägger du till en OnValueChanged hanterare till egenskapen. Mer information finns i Svara på och sprida ändringar.

Example

I följande exempel uppdateras en egenskap när en domänrelation instansieras för att länka två element. Regeln utlöses inte bara när användaren skapar en länk i ett diagram, utan även om programkoden skapar en länk.

Om du vill testa det här exemplet skapar du en DSL med hjälp av lösningsmallen Uppgiftsflöde och infogar följande kod i en fil i Dsl-projektet. Skapa och kör lösningen och öppna exempelfilen i felsökningsprojektet. Rita en kommentarslänk mellan en kommentarsform och ett flödeselement. Texten i kommentaren uppdateras för att rapportera om det senaste elementet som du har anslutit den till.

I praktiken skulle du vanligtvis skriva en DeleteRule för varje 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();
    }
  }

}