Правила распространяют изменения в пределах модели

Вы можете создать правило хранилища для распространения изменений с одного элемента на другой в пакете SDK для визуализации и моделирования (VMSDK). При изменении любого элемента в Магазине правила должны выполняться, как правило, при фиксации самой внешней транзакции. Существуют различные типы правил для различных видов событий, таких как добавление элемента или его удаление. Правила можно присоединить к определенным типам элементов, фигур или схем. Многие встроенные функции определяются правилами: например, правила гарантируют, что схема обновляется при изменении модели. Вы можете настроить язык для конкретного домена, добавив собственные правила.

Правила хранения особенно полезны для распространения изменений внутри хранилища, то есть изменений в элементах модели, отношениях, фигурах или соединителях и их свойствах домена. Правила не выполняются, когда пользователь вызывает команды Undo или Redo. Вместо этого диспетчер транзакций гарантирует, что содержимое хранилища восстановлено в правильном состоянии. Если вы хотите распространить изменения на ресурсы за пределами хранилища, используйте события магазина. Дополнительные сведения см. в разделе Обработчики событий, распространяющие изменения за пределами модели.

Например, предположим, что необходимо указать, что всякий раз, когда пользователь (или код) создает новый элемент типа ExampleDomainClass, в другой части модели создается дополнительный элемент другого типа. Можно написать AddRule и связать его с ExampleDomainClass. Код будет написан в правиле, чтобы создать дополнительный элемент.

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

Примечание.

Код правила должен изменить состояние только элементов в Магазине; То есть правило должно изменять только элементы модели, связи, фигуры, соединители, схемы или их свойства. Если вы хотите распространить изменения на ресурсы за пределами хранилища, определите события магазина. Дополнительные сведения см. в разделе Обработчики событий, распространяющие изменения за пределами модели.

Определение правила

  1. Определите правило в качестве префикса класса, префиксаемого атрибутом RuleOn . Атрибут связывает правило с одним из классов домена, связей или элементов схемы. Правило будет применяться к каждому экземпляру этого класса, который может быть абстрактным.

  2. Зарегистрируйте правило, добавив его в набор, возвращенный GetCustomDomainModelTypes() в классе модели домена.

  3. Наследуйте класс правил от одного из абстрактных классов Rule и напишите код метода выполнения.

    В следующих разделах данные шаги описаны более подробно.

Определение правила в классе домена

  • В пользовательском файле кода определите класс и префикс его атрибутом RuleOnAttribute :

    [RuleOn(typeof(ExampleElement),
         // Usual value - but required, because it is not the default:
         FireTime = TimeToFire.TopLevelCommit)]
    class MyRule ...
    
    
  • Тип субъекта в первом параметре может быть классом домена, связью домена, фигурой, соединителем или схемой. Как правило, правила применяются к классам домена и связям.

    Обычно TopLevelCommitэто FireTime . Это гарантирует, что правило выполняется только после внесения всех основных изменений транзакции. Альтернативные варианты являются встроенными, которые выполняют правило вскоре после изменения; и LocalCommit, который выполняет правило в конце текущей транзакции (которая может не быть внешней). Вы также можете задать приоритет правила, чтобы повлиять на его порядок в очереди, но это ненадежный метод достижения необходимого результата.

  • Можно указать абстрактный класс в качестве типа субъекта.

  • Правило применяется ко всем экземплярам класса темы.

  • Значением по умолчанию является FireTime TimeToFire.TopLevelCommit. Это приводит к выполнению правила при фиксации самой внешней транзакции. Альтернативой является TimeToFire.Inline. Это приводит к тому, что правило будет выполнено вскоре после события триггера.

Регистрация правила

  • Добавьте класс правил в список типов, возвращаемых GetCustomDomainModelTypes в модели домена:

    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();
       }
     }
    
    
  • Если вы не уверены в имени класса модели домена, просмотрите файл Dsl\GeneratedCode\DomainModel.cs

  • Напишите этот код в пользовательский файл кода в проекте DSL.

Написание кода правила

  • Наследует класс правил от одного из следующих базовых классов:

    Базовый класс Триггер
    AddRule Добавляется элемент, ссылка или фигура.

    Используйте это для обнаружения новых связей в дополнение к новым элементам.
    ChangeRule Значение свойства домена изменяется. Аргумент метода предоставляет старые и новые значения.

    Для фигур это правило активируется при изменении встроенного AbsoluteBounds свойства, если фигура перемещается.

    Во многих случаях удобнее переопределить OnValueChanged или OnValueChanging в обработчике свойств. Эти методы вызываются непосредственно перед и после изменения. Напротив, правило обычно выполняется в конце транзакции. Дополнительные сведения см. в разделе "Обработчики изменений значения свойства домена". Примечание. Это правило не активируется при создании или удалении ссылки. Вместо этого напишите AddRule и для DeleteRule связи домена.
    DeletingRule Активируется при удалении элемента или ссылки. Свойство ModelElement.Is Delete имеет значение true до конца транзакции.
    DeleteRule Выполняется при удалении элемента или ссылки. Правило выполняется после выполнения всех остальных правил, включая удалениеRules. ModelElement.Is Delete имеет значение false, и ModelElement.IsDeleted имеет значение true. Чтобы разрешить последующее отмена, элемент не удаляется из памяти, но удаляется из Store.ElementDirectory.
    MoveRule Элемент перемещается из одной секции хранилища в другую.

    (Обратите внимание, что это не связано с графической позицией фигуры.)
    RolePlayerChangeRule Это правило применяется только к отношениям домена. Он активируется при явном назначении элемента модели в любой части ссылки.
    RolePlayerPositionChangeRule Активируется при изменении порядка ссылок на элемент или из нее с помощью методов MoveBefore или MoveToIndex по ссылке.
    TransactionBeginningRule Выполняется при создании транзакции.
    TransactionCommittingRule Выполняется, когда транзакция будет зафиксирована.
    TransactionRollingBackRule Выполняется при откате транзакции.
  • Каждый класс имеет метод, который переопределяется. Введите override его в класс, чтобы обнаружить его. Параметр этого метода определяет измененный элемент.

    Обратите внимание на следующие моменты о правилах:

  1. Набор изменений в транзакции может активировать множество правил. Обычно правила выполняются при фиксации самой внешней транзакции. Они выполняются в неопределенном порядке.

  2. Правило всегда выполняется внутри транзакции. Поэтому для внесения изменений не нужно создавать новую транзакцию.

  3. Правила не выполняются при откате транзакции или при выполнении операций отмены или повтора. Эти операции сбрасывают все содержимое Магазина в предыдущее состояние. Таким образом, если правило изменяет состояние ничего за пределами Магазина, оно может не поддерживать синхронизм с содержимым Магазина. Чтобы обновить состояние за пределами Магазина, лучше использовать события. Дополнительные сведения см. в разделе Обработчики событий, распространяющие изменения за пределами модели.

  4. Некоторые правила выполняются при загрузке модели из файла. Чтобы определить, выполняется ли загрузка или сохранение, используйте store.TransactionManager.CurrentTransaction.IsSerializing.

  5. Если код правила создает больше триггеров правил, они будут добавлены в конец списка срабатываний и будут выполнены до завершения транзакции. DeletedRules выполняются после всех остальных правил. Одно правило может выполняться несколько раз в транзакции, один раз для каждого изменения.

  6. Для передачи информации в правила и из нее TransactionContextможно хранить сведения. Это просто словарь, который поддерживается во время транзакции. Он удаляется, когда транзакция заканчивается. Аргументы событий в каждом правиле предоставляют доступ к нему. Помните, что правила не выполняются в предсказуемом порядке.

  7. Используйте правила после рассмотрения других альтернатив. Например, если вы хотите обновить свойство при изменении значения, рассмотрите возможность использования вычисляемого свойства. Если вы хотите ограничить размер или расположение фигуры, используйте параметр BoundsRule. Если вы хотите ответить на изменение значения свойства, добавьте OnValueChanged обработчик в свойство. Дополнительные сведения см. в статье "Реагирование на изменения" и "Распространение изменений".

Пример

В следующем примере свойство обновляется при создании экземпляра связи домена для связывания двух элементов. Правило будет активировано не только при создании ссылки на схеме, но и при создании ссылки на код программы.

Чтобы проверить этот пример, создайте DSL с помощью шаблона решения потока задач и вставьте следующий код в файл в проект Dsl. Создайте и запустите решение и откройте пример файла в проекте отладки. Нарисуйте ссылку примечания между фигурой "Комментарий" и элементом потока. Текст в примечании изменяется в отчете о последнем элементе, к которому вы подключили его.

На практике обычно создается deleteRule для каждого 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();
    }
  }

}