Compartir a través de


Las reglas propagan cambios dentro del modelo

Puede crear una regla de almacén para propagar un cambio de un elemento a otro en el SDK de visualización y modelado (VMSDK). Cuando se produce un cambio en cualquier elemento de Store, las reglas se programan para ejecutarse, normalmente cuando se confirma la transacción más externa. Hay diferentes tipos de reglas para diferentes tipos de eventos, como agregar un elemento o eliminarlo. Puede adjuntar reglas a tipos específicos de elementos, formas o diagramas. Muchas características integradas se definen mediante reglas: por ejemplo, las reglas garantizan que se actualice un diagrama cuando cambia el modelo. Puede personalizar el idioma específico del dominio agregando sus propias reglas.

Las reglas de almacenamiento son especialmente útiles para propagar los cambios dentro del almacén, es decir, los cambios en elementos de modelo, relaciones, formas o conectores, y sus propiedades de dominio. Las reglas no se ejecutan cuando el usuario invoca los comandos Deshacer o Rehacer. En su lugar, el administrador de transacciones garantiza que el contenido del almacén se restaure al estado correcto. Si quiere propagar los cambios a los recursos fuera de la tienda, use Eventos de la Tienda. Para obtener más información, vea Controladores de eventos propagan cambios fuera del modelo.

Por ejemplo, supongamos que desea especificar que cada vez que el usuario (o el código) cree un nuevo elemento de tipo ExampleDomainClass, se crea un elemento adicional de otro tipo en otra parte del modelo. Puede escribir un AddRule y asociarlo a ExampleDomainClass. Escribiría código en la regla para crear el elemento adicional.

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:

El código de una regla debe cambiar el estado solo de los elementos dentro de store; es decir, la regla solo debe cambiar los elementos del modelo, las relaciones, las formas, los conectores, los diagramas o sus propiedades. Si quiere propagar los cambios a los recursos fuera de la tienda, defina Eventos de la tienda. Para obtener más información, vea Controladores de eventos propagan cambios fuera del modelo.

Para definir una regla

  1. Defina la regla como una clase con el prefijo del atributo RuleOn. El atributo asocia la regla a uno de los elementos de diagrama, relaciones o clases de dominio. La regla se aplicará a cada instancia de esta clase, que puede ser abstracta.

  2. Registre la regla agregándola al conjunto devuelto por GetCustomDomainModelTypes() en la clase de modelo de dominio.

  3. Derive la clase de regla de una de las clases Rule abstractas y escriba el código del método de ejecución.

    En las secciones siguientes se describen estos pasos con más detalle.

Para definir una regla en una clase de dominio

  • En un archivo de código personalizado, defina una clase y prefíjela con el atributo RuleOnAttribute.

    [RuleOn(typeof(ExampleElement),
         // Usual value - but required, because it is not the default:
         FireTime = TimeToFire.TopLevelCommit)]
    class MyRule ...
    
    
  • El tipo de asunto del primer parámetro puede ser una clase de dominio, una relación de dominio, una forma, un conector o un diagrama. Normalmente, se aplican reglas a las clases de dominio y las relaciones.

    El FireTime está normalmente TopLevelCommit. Esto garantiza que la regla se ejecute solo después de que se hayan realizado todos los cambios principales de la transacción. Las alternativas son Inline, que ejecuta la regla inmediatamente después del cambio; y LocalCommit, que ejecuta la regla al final de la transacción actual (que podría no ser la más externa). También puede establecer la prioridad de una regla para que afecte a su ordenación en la cola, pero se trata de un método no confiable para lograr el resultado que necesita.

  • Puede especificar una clase abstracta como el tipo de asunto.

  • La regla se aplica a todas las instancias de la clase sujeto.

  • El valor predeterminado de FireTime es TimeToFire.TopLevelCommit. Esto hace que la regla se ejecute cuando se confirma la transacción más externa. Una alternativa es TimeToFire.Inline. Esto hace que la regla se ejecute poco después del evento desencadenador.

Para registrar la regla

  • Agregue la clase de regla a la lista de tipos devueltos por GetCustomDomainModelTypes en el modelo de 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();
       }
     }
    
    
  • Si no está seguro del nombre de la clase de modelo de dominio, busque dentro del archivo Dsl\GeneratedCode\DomainModel.cs

  • Escriba este código en un archivo de código personalizado en el proyecto DSL.

Para escribir el código de la regla

  • Derive la clase de regla de una de las siguientes clases base:

    Clase base Trigger
    AddRule Se agrega un elemento, un vínculo o una forma.

    Úselo para detectar nuevas relaciones, además de nuevos elementos.
    ChangeRule Se cambia un valor de propiedad de dominio. El argumento de método proporciona los valores antiguos y nuevos.

    En el caso de las formas, esta regla se desencadena cuando cambia la propiedad integrada AbsoluteBounds , si se mueve la forma.

    En muchos casos, es más conveniente invalidar OnValueChanged o OnValueChanging en el controlador de propiedades. Estos métodos se invocan inmediatamente antes y después del cambio. Por el contrario, la regla normalmente se ejecuta al final de la transacción. Para obtener más información, vea Controladores de cambio de valor de propiedad de dominio. Nota: Esta regla no se desencadena cuando se crea o elimina un vínculo. En su lugar, escriba un elemento AddRule y un DeleteRule para la relación de dominio.
    DeletingRule Se desencadena cuando un elemento o vínculo está a punto de eliminarse. La propiedad ModelElement.IsDeleting es verdadero hasta el final de la transacción.
    DeleteRule Se realiza cuando se ha eliminado un elemento o vínculo. La regla se ejecuta después de ejecutar todas las demás reglas, incluido DeletingRules. ModelElement.IsDeleting es falso y ModelElement.IsDeleted es verdadero. Para permitir un deshacer posterior, el elemento no se quita realmente de la memoria, pero se quita de Store.ElementDirectory.
    MoveRule Un elemento se mueve de una partición de almacén a otra.

    (Observe que esto no está relacionado con la posición gráfica de una forma).
    RolePlayerChangeRule Esta regla solo se aplica a las relaciones de dominio. Se desencadena si asigna explícitamente un elemento de modelo al final de un vínculo.
    RolePlayerPositionChangeRule Se desencadena cuando se cambia el orden de los vínculos a o desde un elemento mediante los métodos MoveBefore o MoveToIndex en un vínculo.
    TransactionBeginningRule Se ejecuta cuando se crea una transacción.
    TransactionCommittingRule Se ejecuta cuando la transacción está a punto de confirmarse.
    TransactionRollingBackRule Se ejecuta cuando la transacción está a punto de revertirse.
  • Cada clase tiene un método que sobrescribes. Escriba override en tu clase para descubrirla. El parámetro de este método identifica el elemento que se está cambiando.

    Observe los siguientes puntos sobre las reglas:

  1. El conjunto de cambios en una transacción podría desencadenar muchas reglas. Normalmente, las reglas se ejecutan cuando se confirma la transacción más externa. Se ejecutan en un orden no especificado.

  2. Una regla siempre se ejecuta dentro de una transacción. Por lo tanto, no es necesario crear una nueva transacción para realizar cambios.

  3. Las reglas no se ejecutan cuando se revierte una transacción ni cuando se realizan las operaciones de Deshacer o Rehacer. Estas operaciones restablecen todo el contenido de store a su estado anterior. Por lo tanto, si su regla cambia el estado de algo fuera de la Tienda, podría no mantenerse en sincronía con el contenido de la Tienda. Para actualizar el estado fuera del Store, es mejor usar los Eventos. Para obtener más información, vea Controladores de eventos propagan cambios fuera del modelo.

  4. Algunas reglas se ejecutan cuando se carga un modelo desde el archivo. Para determinar si la carga o el guardado están en curso, use store.TransactionManager.CurrentTransaction.IsSerializing.

  5. Si el código de la regla crea más desencadenadores, se agregarán al final de la lista de activación y se ejecutarán antes de que se complete la transacción. DeletedRules se ejecutan después de todas las otras reglas. Una regla se puede ejecutar muchas veces en una transacción, una vez por cada cambio.

  6. Para pasar información hacia y desde las reglas, puede almacenar información en TransactionContext. Se trata de un diccionario que se mantiene durante la transacción. Se elimina cuando finaliza la transacción. Los argumentos de evento de cada regla proporcionan acceso a él. Recuerde que las reglas no se ejecutan en un orden predecible.

  7. Use reglas después de considerar otras alternativas. Por ejemplo, si desea actualizar una propiedad cuando cambie un valor, considere la posibilidad de usar una propiedad calculada. Si desea restringir el tamaño o la ubicación de una forma, use .BoundsRule Si desea responder a un cambio en un valor de propiedad, agregue un OnValueChanged controlador a la propiedad . Para obtener más información, vea Responder a los cambios y propagarlos.

Example

En el ejemplo siguiente se actualiza una propiedad cuando se crea una instancia de una relación de dominio para vincular dos elementos. La regla se desencadenará no solo cuando el usuario cree un vínculo en un diagrama, sino también si el código de programa crea un vínculo.

Para probar este ejemplo, cree un DSL mediante la plantilla de solución Flujo de tareas e inserte el código siguiente en un archivo del proyecto dsl. Compile y ejecute la solución y abra el archivo de ejemplo en el proyecto de depuración. Dibuje un vínculo de comentario entre una forma Comment y un elemento de flujo. El comentario cambia para informar sobre el elemento más reciente que has conectado.

En la práctica, normalmente escribiría un DeleteRule para cada 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();
    }
  }

}