Partilhar via


Regras propagam alterações dentro do modelo

Você pode criar uma regra de repositório para propagar uma alteração de um elemento para outro no VMSDK (SDK de Visualização e Modelagem). Quando ocorre uma alteração em qualquer elemento do repositório, a execução das regras é agendada, geralmente quando a transação mais externa é confirmada. Há diferentes tipos de regras para diferentes tipos de eventos, como adicionar um elemento ou excluí-lo. Você pode anexar regras a tipos específicos de elementos, formas ou diagramas. Muitos recursos internos são definidos por regras, por exemplo, as regras garantem que um diagrama seja atualizado quando o modelo é alterado. Você pode personalizar a linguagem específica de domínio adicionando suas regras.

As regras de repositório são particularmente úteis para propagar alterações dentro do repositório, ou seja, alterações em elementos de modelo, relações, formas ou conectores e as respectivas propriedades de domínio. As regras não são executadas quando o usuário invoca os comandos Desfazer ou Refazer. Em vez disso, o gerenciador de transação garante que o conteúdo do repositório seja restaurado para o estado correto. Para propagar alterações para recursos fora do repositório, use Eventos de Repositório. Para obter mais informações, confira Manipuladores de eventos propagam alterações fora do modelo.

Por exemplo, digamos que você queira especificar que sempre que o usuário (ou seu código) cria um elemento do tipo ExampleDomainClass, um elemento adicional de outro tipo é criado em outra parte do modelo. Você pode escrever uma AddRule e associá-la a ExampleDomainClass. Você escreveria código na regra para criar o 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();
   }
 }
}

Observação

O código de uma regra deve alterar o estado somente dos elementos dentro do repositório, ou seja, a regra deve alterar apenas elementos de modelo, relações, formas, conectores, diagramas ou as respectivas propriedades. Para propagar alterações para recursos fora do repositório, defina Eventos de Repositório. Para obter mais informações, confira Manipuladores de eventos propagam alterações fora do modelo.

Para definir uma regra

  1. Defina a regra como uma classe prefixada com o atributo RuleOn. O atributo associa a regra a uma de suas classes de domínio, relações ou elementos de diagrama. A regra será aplicada a todas as instâncias dessa classe, que podem ser abstratas.

  2. Registre a regra adicionando-a ao conjunto retornado por GetCustomDomainModelTypes() em sua classe de modelo de domínio.

  3. Derive a classe da regra de uma das classes de Regra abstratas e escreva o código do método de execução.

    As seções a seguir descrevem essas etapas mais detalhadamente.

Para definir uma regra em uma classe de domínio

  • Em um arquivo de código personalizado, defina uma classe e prefixe-a com o atributo RuleOnAttribute:

    [RuleOn(typeof(ExampleElement),
         // Usual value - but required, because it is not the default:
         FireTime = TimeToFire.TopLevelCommit)]
    class MyRule ...
    
    
  • O tipo de entidade no primeiro parâmetro pode ser uma classe de domínio, uma relação de domínio, uma forma, um conector ou um diagrama. Normalmente, você aplica regras a classes de domínio e relações.

    O FireTime geralmente é TopLevelCommit. Isso garante que a regra seja executada somente depois que todas as alterações primárias da transação tiverem sido feitas. As alternativas são Embutido, que executa a regra logo após a alteração; e LocalCommit, que executa a regra no final da transação atual (que pode não ser a mais externa). Você também pode definir a prioridade de uma regra para afetar sua ordenação na fila, mas esse é um método não confiável para alcançar o resultado necessário.

  • Você pode especificar uma classe abstrata como tipo de entidade.

  • A regra se aplica a todas as instâncias da classe de entidade.

  • O valor padrão para FireTime é TimeToFire.TopLevelCommit. Isso faz com que a regra seja executada quando a transação mais externa é confirmada. Uma alternativa é TimeToFire.Inline. Isso faz com que a regra seja executada logo após o evento gatilho.

Para registrar a regra

  • Adicione a classe de regra à lista de tipos retornados por GetCustomDomainModelTypes no modelo de domínio:

    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 não tiver certeza do nome da classe de modelo de domínio, examine dentro do arquivo Dsl\GeneratedCode\DomainModel.cs

  • Escreva esse código em um arquivo de código personalizado no projeto de DSL.

Para escrever o código da regra

  • Derive a classe da regra de uma das seguintes classes base:

    Classe base Gatilho
    AddRule Um elemento, um link ou uma forma é adicionada.

    Use isso para detectar novas relações, bem como novos elementos.
    ChangeRule Um valor de propriedade de domínio é alterado. O argumento de método fornece os valores antigo e novo.

    Para formas, essa regra é disparada quando a propriedade interna AbsoluteBounds é alterada, se a forma é movida.

    Em muitos casos, é mais conveniente substituir OnValueChanged ou OnValueChanging no manipulador de propriedades. Esses métodos são chamados imediatamente antes e depois da alteração. Por outro lado, a regra geralmente é executada no final da transação. Para obter mais informações, consulte Manipuladores de alteração de valor de propriedade de domínio. Observação: essa regra não é disparada quando um link é criado ou excluído. Em vez disso, escreva um AddRule e um DeleteRule para o relacionamento de domínio.
    DeletingRule Acionada quando um elemento ou link está prestes a ser excluído. A propriedade ModelElement.IsDeleting é verdadeira até o final da transação.
    DeleteRule Executada quando um elemento ou link foi excluído. A regra é executada depois de todas as outras, incluindo DeletingRules. ModelElement.IsDeleting é falso e ModelElement.IsDeleted é verdadeiro. Para permitir Desfazer posteriormente, o elemento não é de fato removido da memória, mas é removido de Store.ElementDirectory.
    MoveRule Um elemento é movido de uma partição do repositório para outra.

    (Observe que isso não está relacionado à posição gráfica de uma forma.)
    RolePlayerChangeRule Essa regra se aplica somente a relacionamentos de domínio. Ela será disparada se você atribuir explicitamente um elemento de modelo a uma das extremidades de um link.
    RolePlayerPositionChangeRule Acionada quando a ordenação de links para ou de um elemento é alterada usando os métodos MoveBefore ou MoveToIndex em um link.
    TransactionBeginningRule Executada quando uma transação é criada.
    TransactionCommittingRule Executada quando a transação está prestes a ser confirmada.
    TransactionRollingBackRule Executada quando a transação está prestes a ser revertida.
  • Cada classe tem um método que você substitui. Digite override na classe para descobri-la. O parâmetro desse método identifica o elemento que está sendo alterado.

    Observe os seguintes pontos sobre as regras:

  1. O conjunto de alterações em uma transação pode disparar muitas regras. Normalmente, as regras são executadas quando a transação mais externa é confirmada. Elas são executadas em uma ordem não especificada.

  2. Uma regra sempre é executada dentro de uma transação. Portanto, você não precisa criar uma transação para fazer alterações.

  3. Regras não são executadas quando uma transação é revertida ou quando as operações Desfazer ou Refazer são executadas. Essas operações redefinem todo o conteúdo do repositório para o estado anterior. Portanto, se a regra alterar o estado de qualquer coisa fora do repositório, ela poderá não manter a sincronização com o conteúdo do repositório. Para atualizar o estado fora do repositório, é melhor usar Eventos. Para obter mais informações, confira Manipuladores de eventos propagam alterações fora do modelo.

  4. Algumas regras são executadas quando um modelo é carregado do arquivo. Para determinar se o carregamento ou salvamento está em andamento, use store.TransactionManager.CurrentTransaction.IsSerializing.

  5. Se o código da regra criar mais gatilhos de regra, eles serão adicionados ao final da lista de disparo e executados antes da conclusão da transação. DeletedRules são executadas após todas as outras regras. Uma regra pode ser executada muitas vezes em uma transação, uma vez para cada alteração.

  6. Para passar informações das regras e para elas, você pode armazenar informações em TransactionContext. Trata-se apenas de um dicionário que é mantido durante a transação. Ele é descartado quando a transação termina. Os argumentos de evento em cada regra fornecem acesso a ele. Lembre-se de que as regras não são executadas em uma ordem previsível.

  7. Use-as depois de considerar outras alternativas. Por exemplo, se quiser atualizar uma propriedade quando um valor for alterado, considere usar uma propriedade calculada. Para restringir o tamanho ou o local de uma forma, use um BoundsRule. Para responder a uma alteração em um valor de propriedade, adicione um manipulador OnValueChanged à propriedade. Para obter mais informações, confira Responder a alterações e propagá-las.

Exemplo

O exemplo a seguir atualiza uma propriedade quando é criada uma instância de um relacionamento de domínio para vincular dois elementos. A regra será disparada não apenas quando o usuário criar um link em um diagrama, mas também se o código do programa criar um link.

Para testar o exemplo, crie uma DSL usando o modelo de solução Fluxo de Tarefas e insira o código a seguir em um arquivo no projeto de DSL. Compile e execute a solução e abra o arquivo de Exemplo no projeto de Depuração. Desenhe um Link de Comentário entre uma forma de Comentário e um elemento de fluxo. O texto no comentário é alterado para relatar o elemento mais recente ao qual você o conectou.

Na prática, você normalmente escreveria uma 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();
    }
  }

}