Le regole propagano le modifiche all'interno del modello

È possibile creare una regola di archiviazione per propagare una modifica da un elemento a un altro in Visualization and Modeling SDK (VMSDK). Quando si verifica una modifica a qualsiasi elemento nello Store, le regole vengono pianificate per l'esecuzione, in genere quando viene eseguito il commit della transazione più esterna. Esistono diversi tipi di regole per diversi tipi di eventi, ad esempio l'aggiunta di un elemento o l'eliminazione. È possibile associare regole a tipi specifici di elementi, forme o diagrammi. Molte funzionalità predefinite sono definite dalle regole: ad esempio, le regole assicurano che un diagramma venga aggiornato quando il modello cambia. È possibile personalizzare il linguaggio specifico del dominio aggiungendo regole personalizzate.

Le regole di archiviazione sono particolarmente utili per propagare le modifiche all'interno dell'archivio, ovvero le modifiche apportate agli elementi del modello, alle relazioni, alle forme o ai connettori e alle relative proprietà di dominio. Le regole non vengono eseguite quando l'utente richiama i comandi Annulla o Ripeti. Il gestore delle transazioni assicura invece che il contenuto dell'archivio venga ripristinato allo stato corretto. Se si desidera propagare le modifiche alle risorse esterne all'archivio, usare Eventi dello Store. Per altre informazioni, vedere Gestori eventi propagare le modifiche all'esterno del modello.

Si supponga, ad esempio, di voler specificare che ogni volta che l'utente (o il codice) crea un nuovo elemento di tipo ExampleDomainClass, viene creato un elemento aggiuntivo di un altro tipo in un'altra parte del modello. È possibile scrivere un addRule e associarlo a ExampleDomainClass. Scrivere codice nella regola per creare l'elemento aggiuntivo.

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

Nota

Il codice di una regola deve modificare lo stato solo degli elementi all'interno dello Store; ovvero, la regola deve modificare solo gli elementi del modello, le relazioni, le forme, i connettori, i diagrammi o le relative proprietà. Se vuoi propagare le modifiche alle risorse esterne all'archivio, definisci Eventi dello Store. Per altre informazioni, vedere Gestori eventi propagare le modifiche all'esterno del modello.

Per definire una regola

  1. Definire la regola come classe preceduta dall'attributo RuleOn . L'attributo associa la regola a una delle classi di dominio, alle relazioni o agli elementi del diagramma. La regola verrà applicata a ogni istanza di questa classe, che può essere astratta.

  2. Registrare la regola aggiungendola al set restituito da GetCustomDomainModelTypes() nella classe del modello di dominio.

  3. Derivare la classe di regole da una delle classi Rule astratte e scrivere il codice del metodo di esecuzione.

    Le sezioni seguenti descrivono questi passaggi in modo più dettagliato.

Per definire una regola in una classe di dominio

  • In un file di codice personalizzato definire una classe e anteporre il prefisso con l'attributo RuleOnAttribute :

    [RuleOn(typeof(ExampleElement),
         // Usual value - but required, because it is not the default:
         FireTime = TimeToFire.TopLevelCommit)]
    class MyRule ...
    
    
  • Il tipo di oggetto nel primo parametro può essere una classe di dominio, una relazione di dominio, una forma, un connettore o un diagramma. In genere, si applicano regole alle classi e alle relazioni di dominio.

    è FireTime in genere TopLevelCommit. Ciò garantisce che la regola venga eseguita solo dopo che sono state apportate tutte le modifiche principali della transazione. Le alternative sono Inline, che esegue la regola subito dopo la modifica; e LocalCommit, che esegue la regola alla fine della transazione corrente (che potrebbe non essere la più esterna). È anche possibile impostare la priorità di una regola per influire sull'ordinamento nella coda, ma si tratta di un metodo inaffidabile per ottenere il risultato richiesto.

  • È possibile specificare una classe astratta come tipo di oggetto.

  • La regola si applica a tutte le istanze della classe subject.

  • Il valore predefinito per FireTime è TimeToFire.TopLevelCommit. In questo modo la regola viene eseguita quando viene eseguito il commit della transazione più esterna. Un'alternativa è TimeToFire.Inline. In questo modo la regola verrà eseguita subito dopo l'evento di attivazione.

Per registrare la regola

  • Aggiungere la classe della regola all'elenco dei tipi restituiti da GetCustomDomainModelTypes nel modello di dominio:

    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();
       }
     }
    
    
  • Se non si è certi del nome della classe del modello di dominio, esaminare il file Dsl\GeneratedCode\DomainModel.cs

  • Scrivere questo codice in un file di codice personalizzato nel progetto DSL.

Per scrivere il codice della regola

  • Derivare la classe di regole da una delle classi di base seguenti:

    Classe base Trigger
    AddRule Viene aggiunto un elemento, un collegamento o una forma.

    Usare questa opzione per rilevare nuove relazioni, oltre ai nuovi elementi.
    ChangeRule Viene modificato un valore della proprietà di dominio. L'argomento del metodo fornisce i valori vecchi e nuovi.

    Per le forme, questa regola viene attivata quando viene modificata la proprietà predefinita AbsoluteBounds , se la forma viene spostata.

    In molti casi, è più conveniente eseguire l'override OnValueChanged o OnValueChanging nel gestore delle proprietà. Questi metodi vengono chiamati immediatamente prima e dopo la modifica. Al contrario, la regola viene in genere eseguita alla fine della transazione. Per altre informazioni, vedere Gestori delle modifiche dei valori delle proprietà di dominio. Nota: questa regola non viene attivata quando viene creato o eliminato un collegamento. Scrivere invece un oggetto AddRule e per DeleteRule la relazione di dominio.
    DeletingRule Attivato quando un elemento o un collegamento sta per essere eliminato. La proprietà ModelElement.IsDeleting è true fino alla fine della transazione.
    DeleteRule Eseguita quando un elemento o un collegamento è stato eliminato. La regola viene eseguita dopo l'esecuzione di tutte le altre regole, incluso DeletingRules. ModelElement.IsDeleting è false e ModelElement.IsDeleted è true. Per consentire un successivo annullamento, l'elemento non viene effettivamente rimosso dalla memoria, ma viene rimosso da Store.ElementDirectory.
    MoveRule Un elemento viene spostato da una partizione di archivio a un'altra.

    Si noti che non è correlato alla posizione grafica di una forma.
    RolePlayerChangeRule Questa regola si applica solo alle relazioni di dominio. Viene attivato se si assegna in modo esplicito un elemento del modello a una delle estremità di un collegamento.
    RolePlayerPositionChangeRule Attivato quando l'ordinamento dei collegamenti da o verso un elemento viene modificato usando i metodi MoveBefore o MoveToIndex su un collegamento.
    TransactionBeginningRule Eseguito quando viene creata una transazione.
    TransactionCommittingRule Eseguito quando la transazione sta per essere sottoposta a commit.
    TransactionRollingBackRule Eseguito quando si sta per eseguire il rollback della transazione.
  • Ogni classe ha un metodo di cui si esegue l'override. Digitare override nella classe per individuarla. Il parametro di questo metodo identifica l'elemento da modificare.

    Si notino i punti seguenti sulle regole:

  1. Il set di modifiche in una transazione potrebbe attivare molte regole. In genere, le regole vengono eseguite quando viene eseguito il commit della transazione più esterna. Vengono eseguite in un ordine non specificato.

  2. Una regola viene sempre eseguita all'interno di una transazione. Pertanto, non è necessario creare una nuova transazione per apportare modifiche.

  3. Le regole non vengono eseguite quando viene eseguito il rollback di una transazione o quando vengono eseguite le operazioni Annulla o Ripeti. Queste operazioni reimpostano tutto il contenuto dello Store sullo stato precedente. Pertanto, se la regola modifica lo stato di qualsiasi elemento all'esterno dello Store, potrebbe non essere sincronizzato con il contenuto dello Store. Per aggiornare lo stato all'esterno dello Store, è preferibile usare eventi. Per altre informazioni, vedere Gestori eventi propagare le modifiche all'esterno del modello.

  4. Alcune regole vengono eseguite quando un modello viene caricato dal file. Per determinare se il caricamento o il salvataggio è in corso, usare store.TransactionManager.CurrentTransaction.IsSerializing.

  5. Se il codice della regola crea più trigger di regola, verranno aggiunti alla fine dell'elenco di attivazione e verranno eseguiti prima del completamento della transazione. DeletedRules viene eseguito dopo tutte le altre regole. Una regola può essere eseguita molte volte in una transazione, una volta per ogni modifica.

  6. Per passare informazioni da e verso regole, è possibile archiviare le informazioni in TransactionContext. Si tratta solo di un dizionario gestito durante la transazione. Viene eliminato al termine della transazione. Gli argomenti dell'evento in ogni regola forniscono l'accesso. Tenere presente che le regole non vengono eseguite in un ordine prevedibile.

  7. Usare le regole dopo aver considerato altre alternative. Ad esempio, se si desidera aggiornare una proprietà quando viene modificato un valore, è consigliabile usare una proprietà calcolata. Se si desidera vincolare le dimensioni o la posizione di una forma, utilizzare un oggetto BoundsRule. Se si desidera rispondere a una modifica in un valore della proprietà, aggiungere un OnValueChanged gestore alla proprietà . Per altre informazioni, vedere Risposta a e propagazione delle modifiche.

Esempio

Nell'esempio seguente viene aggiornata una proprietà quando viene creata un'istanza di una relazione di dominio per collegare due elementi. La regola verrà attivata non solo quando l'utente crea un collegamento in un diagramma, ma anche se il codice del programma crea un collegamento.

Per testare questo esempio, creare un linguaggio DSL usando il modello di soluzione Flusso attività e inserire il codice seguente in un file nel progetto Dsl. Compilare ed eseguire la soluzione e aprire il file di esempio nel progetto debug. Disegnare un collegamento di commento tra una forma Commento e un elemento di flusso. Il testo nel commento cambia per segnalare l'elemento più recente a cui è stata connessa.

In pratica, in genere si scriverà DeleteRule per ogni 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();
    }
  }

}