Partilhar via


Validação em um idioma específico do domínio

Como o autor de um idioma específico do domínio (DSL), você pode definir restrições de validação para verificar se o modelo criado pelo usuário é significativo.Por exemplo, se o seu DSL permite que os usuários podem desenhar uma árvore genealógica de pessoas e seus ancestrais, você poderia escrever uma restrição que garante que as crianças tenham datas de nascimento após seus pais.

Você pode ter restrições de validação executado quando o modelo é salvo, quando ele é aberto e quando o usuário executa explicitamente o Validar comando de menu.Você também pode executar validação sob controle do programa.Por exemplo, você pode executar a validação em resposta a uma alteração em um valor da propriedade ou o relacionamento.

A validação é particularmente importante se você estiver criando modelos de texto ou outras ferramentas que processam modelos dos seus usuários.A validação garante que os modelos de cumprir as pré-condições assumidas dessas ferramentas.

Observação de cuidadoCuidado

Você também pode permitir que as restrições de validação seja definido em extensões separadas para seu DSL, juntamente com os comandos de menu de extensão e manipuladores de gesto.Os usuários podem optar por instalar essas extensões com seu DSL.Para obter mais informações, consulte Estender seu DSL usando MEF.

Executando validação

Quando um usuário está editando um modelo, ou seja, uma instância da sua linguagem específica de domínio, as seguintes ações podem executar validação:

  • O diagrama com o botão direito e selecione Validar todos os.

  • Clique com o botão direito no nó superior no Gerenciador de sua DSL e selecione Validar todos os

  • Salve o modelo.

  • Abra o modelo.

  • Além disso, você pode escrever código de programa que executa a validação, por exemplo, como parte de um comando de menu ou em resposta a uma alteração.

Quaisquer erros de validação será exibido na Error List janela.O usuário pode clicar duas vezes uma mensagem de erro para selecionar os elementos de modelo são a causa do erro.

Definir restrições de validação

Você pode definir restrições de validação, adicionando os métodos de validação para as classes de domínio ou os relacionamentos de seu DSL.Quando a validação é executada pelo usuário ou em controle de programa, alguns ou todos os métodos de validação são executados.Cada método é aplicado a cada instância de sua classe, e pode haver vários métodos de validação em cada classe.

Cada método de validação relata os erros que encontrar.

ObservaçãoObservação

Métodos de validação de relatam erros, mas não alteram o modelo.Se você quiser ajustar ou impedir que determinadas alterações, consulte alternativas à validação.

Para definir uma restrição de validação

  1. Ativar a validação na Editor\Validation nó:

    1. Abra Dsl\DslDefinition.dsl.

    2. No Explorer DSL, expanda o Editor nó e selecione validação.

    3. Na janela Properties, defina a usa propriedades para true.É mais conveniente definir todas essas propriedades.

    4. Clique em Transformar todos os modelos de na barra de ferramentas do Solution Explorer.

  2. Grave definições de classe parcial para uma ou mais das suas classes de domínio ou relações de domínio.Essas definições de gravação em um novo arquivo de código na Dsl project.

  3. Cada classe com esse atributo de prefixo:

    [ValidationState(ValidationState.Enabled)]
    
    • Por padrão, esse atributo também permitirá a validação para classes derivadas.Se você quiser desativar validação para uma classe derivada específica, você pode usar ValidationState.Disabled.
  4. Adicione métodos de validação para as classes.Cada método de validação pode ter qualquer nome, mas ter um parâmetro do tipo ValidationContext.

    Ele deve ser prefixado com um ou mais ValidationMethod atributos:

    [ValidationMethod (ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu ) ]
    

    Os ValidationCategories especificar quando o método for executado.

Por exemplo:

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;

// Allow validation methods in this class:
[ValidationState(ValidationState.Enabled)]
// In this DSL, ParentsHaveChildren is a domain relationship
// from Person to Person:
public partial class ParentsHaveChildren
{
  // Identify the method as a validation method:
  [ValidationMethod
  ( // Specify which events cause the method to be invoked:
    ValidationCategories.Open // On file load.
  | ValidationCategories.Save // On save to file.
  | ValidationCategories.Menu // On user menu command.
  )]
  // This method is applied to each instance of the 
  // type (and its subtypes) in a model: 
  private void ValidateParentBirth(ValidationContext context)   
  {
    // In this DSL, the role names of this relationship
    // are "Child" and "Parent": 
     if (this.Child.BirthYear < this.Parent.BirthYear 
        // Allow user to leave the year unset:
        && this.Child.BirthYear != 0)
      {
        context.LogError(
             // Description:
                       "Child must be born after Parent",
             // Unique code for this error:
                       "FAB001ParentBirthError", 
              // Objects to select when user double-clicks error:
                       this.Child, 
                       this.Parent);
    }
  }

Observe os seguintes pontos sobre este código:

  • Você pode adicionar métodos de validação às classes de domínio ou relações de domínio.O código para esses tipos está em Dsl\Generated Code\Domain*.cs.

  • Cada método de validação é aplicado a cada instância de sua classe e suas subclasses.No caso de uma relação de domínio, cada instância é um vínculo entre dois elementos de modelo.

  • Métodos de validação não são aplicados em alguma ordem especificada, e cada método não será aplicado às instâncias de sua classe em qualquer ordem previsível.

  • É prática geralmente muito ruim para um método de validação atualizar o armazenamento de conteúdo, porque isso poderia levar a resultados inconsistentes.Em vez disso, o método reporte qualquer erro chamando context.LogError, LogWarning ou LogInfo.

  • Na chamada LogError, você pode fornecer uma lista de elementos de modelo ou vínculos de relacionamento serão selecionados quando o usuário clica duas vezes a mensagem de erro.

  • Para obter informações sobre como ler o modelo de código de programa, consulte Navegando e atualizando um modelo de código de programa.

O exemplo aplica o modelo de domínio a seguir.O relacionamento ParentsHaveChildren tem funções que são nomeadas filho e pai.

Diagrama de definição de DSL - modelo de árvore genealógica

Categorias de validação

No ValidationMethod atributo, especifique quando o método de validação deve ser executado.

<strong>Categoria</strong>

Execução

Menu

Quando o usuário aciona o comando de menu Validate.

Open

Quando o arquivo de modelo é aberto.

Save

Quando o arquivo é salvo.Se houver erros de validação, o usuário terá a opção de salvar o cancelamento de operação.

Load

Quando o arquivo é salvo.Se houver erros de métodos nesta categoria, o usuário é avisado de que talvez não seja possível reabrir o arquivo.

Use esta categoria para métodos de validação que testa para IDs ou nomes duplicados, ou outras condições que poderiam causar erros de carregamento.

Custom

Quando o método ValidateCustom é chamado.Validações desta categoria podem ser chamadas somente a partir do código de programa.

Para obter mais informações, consulte Categorias de validação personalizada.

Métodos de validação do lugar onde

Geralmente, você pode obter o mesmo efeito, colocando um método de validação em um tipo.Por exemplo, você poderia adicionar um método para a classe Person em vez do relacionamento ParentsHaveChildren e tê-lo a fazer uma iteração através de links:

[ValidationState(ValidationState.Enabled)]
public partial class Person
{[ValidationMethod
 ( ValidationCategories.Open 
 | ValidationCategories.Save
 | ValidationCategories.Menu
 )
]
  private void ValidateParentBirth(ValidationContext context)   
  {
    // Iterate through ParentHasChildren links:
    foreach (Person parent in this.Parents)
    {
        if (this.BirthYear <= parent.BirthYear)
        { ...

Restrições de validação de agregação. Para aplicar a validação em uma ordem previsível, defina um método de validação de único em uma classe de proprietário, como o elemento raiz do seu modelo.Essa técnica também permite agregar vários relatórios de erro em uma única mensagem.

As desvantagens são que o método combinado é menos fácil de gerenciar e que as restrições devem todos a ter o mesmo ValidationCategories.Portanto, recomendamos que você mantenha todas as restrições em um método separado se possível.

Passando os valores no cache de contexto. O parâmetro de contexto tem um dicionário nos quais você pode colocar valores arbitrários.O dicionário persiste durante a vida útil da execução de validação.Um método de validação específica poderia, por exemplo, manter uma contagem de erro no contexto e usá-lo para evitar a saturação da janela de erros com mensagens repetidas.Por exemplo:

List<ParentsHaveChildren> erroneousLinks;
if (!context.TryGetCacheValue("erroneousLinks", out erroneousLinks))
erroneousLinks = new List<ParentsHaveChildren>();
erroneousLinks.Add(this);
context.SetCacheValue("erroneousLinks", erroneousLinks);
if (erroneousLinks.Count < 5) { context.LogError( ... ); }

Validação de multiplicidades

Métodos de validação para verificar a multiplicidade mínima são gerados automaticamente para seu DSL.O código é gravado para Dsl\Generated Code\MultiplicityValidation.cs.Esses métodos entrarão em vigor quando você ativa a validação na Editor\Validation o nó no Explorer DSL.

Se você definir a multiplicidade de uma função de uma relação de domínio será 1... * ou 1.. 1, mas o usuário não cria um link desse relacionamento, será exibida uma mensagem de erro de validação.

Por exemplo, se o seu DSL tem classes pessoa e cidade e uma relação PersonLivesInTown com uma relação de 1... * o papel de cidade, em seguida, para cada pessoa que não tenha nenhum Town, uma mensagem de erro será exibida.

Executando validação do código de programa

Você pode executar validação acessando ou criando um ValidationController.Se desejar que os erros a serem exibidos para o usuário na janela de erros, use o ValidationController associado ao DocData do seu diagrama.Por exemplo, se você estiver escrevendo um comando de menu, CurrentDocData.ValidationController está disponível na classe de conjunto de comando:

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Modeling.Shell;
...
partial class MyLanguageCommandSet 
{
  private void OnMenuMyContextMenuCommand(object sender, EventArgs e) 
  { 
   ValidationController controller = this.CurrentDocData.ValidationController; 
...

Para obter mais informações, consulte Como: adicionar um comando ao Menu de atalho.

Você também pode criar um controlador separado de validação e gerenciar os erros por conta própria.Por exemplo:

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Modeling.Shell;
...
Store store = ...;
VsValidationController validator = new VsValidationController(s);
// Validate all elements in the Store:
if (!validator.Validate(store, ValidationCategories.Save))
{
  // Deal with errors:
  foreach (ValidationMessage message in validator.ValidationMessages) { ... }
}

Executando validação quando ocorre uma alteração

Se você desejar certificar-se de que o usuário é avisado imediatamente se o modelo se tornará inválido, você pode definir um evento de armazenamento que executa a validação.Para obter mais informações sobre eventos de armazenamento, consulte Manipuladores de eventos propagam alterações fora do modelo.

Além do código de validação, adicionar um arquivo de código personalizado para seu DslPackage o projeto, com conteúdo semelhante ao exemplo a seguir.Esse código usa a ValidationController que é anexado ao documento.Este controlador exibe os erros de validação na Visual Studio lista de erros.

using System;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Validation;
namespace Company.FamilyTree
{
  partial class FamilyTreeDocData // Change name to your DocData.
  {
    // Register the store event handler: 
    protected override void OnDocumentLoaded()
    {
      base.OnDocumentLoaded();
      DomainClassInfo observedLinkInfo = this.Store.DomainDataDirectory
         .FindDomainClass(typeof(ParentsHaveChildren));
      DomainClassInfo observedClassInfo = this.Store.DomainDataDirectory
         .FindDomainClass(typeof(Person));
      EventManagerDirectory events = this.Store.EventManagerDirectory;
      events.ElementAdded
         .Add(observedLinkInfo, new EventHandler<ElementAddedEventArgs>(ParentLinkAddedHandler));
      events.ElementDeleted.Add(observedLinkInfo, new EventHandler<ElementDeletedEventArgs>(ParentLinkDeletedHandler));
      events.ElementPropertyChanged.Add(observedClassInfo, new EventHandler<ElementPropertyChangedEventArgs>(BirthDateChangedHandler));
    }
    // Handler will be called after transaction that creates a link:
    private void ParentLinkAddedHandler(object sender,
                                ElementAddedEventArgs e)
    {
      this.ValidationController.Validate(e.ModelElement,
           ValidationCategories.Save);
    }
    // Called when a link is deleted:
    private void ParentLinkDeletedHandler(object sender, 
                                ElementDeletedEventArgs e)
    {
      // Don't apply validation to a deleted item! 
      // - Validate store to refresh the error list.
      this.ValidationController.Validate(this.Store,
           ValidationCategories.Save);
    }
    // Called when any property of a Person element changes:
    private void BirthDateChangedHandler(object sender,
                      ElementPropertyChangedEventArgs e)
    {
      Person person = e.ModelElement as Person;
      // Not interested in changes in other properties:
      if (e.DomainProperty.Id != Person.BirthYearDomainPropertyId)
          return;

      // Validate all parent links to and from the person:
      this.ValidationController.Validate(
        ParentsHaveChildren.GetLinksToParents(person)
        .Concat(ParentsHaveChildren.GetLinksToChildren(person))
        , ValidationCategories.Save);
    }
  }
} 

Os manipuladores são também chamados após desfazer ou refazer operações que afetam os links ou elementos.

Categorias de validação personalizada

Além de categorias de validação padrão, como, por exemplo, Menu e aberto, você pode definir suas próprias categorias.Você pode chamar essas categorias a partir do código do programa.O usuário não pode invocá-los diretamente.

Um uso típico para categorias personalizadas é definir uma categoria que testa se o modelo satisfaz as pré-condições de uma determinada ferramenta.

Para adicionar um método de validação para uma determinada categoria, prefixo com um atributo como este:

[ValidationMethod(CustomCategory = "PreconditionsForGeneratePartsList")]
[ValidationMethod(ValidationCategory.Menu)] 
private void TestForCircularLinks(ValidationContext context) 
{...}
ObservaçãoObservação

Você poderá colocar um método com tantos [ValidationMethod()] atributos como você deseja.Você pode adicionar um método para as categorias padrão e personalizadas.

Para chamar uma validação personalizada:

// Invoke all validation methods in a custom category: 
validationController.ValidateCustom
  (store, // or a list of model elements
   "PreconditionsForGeneratePartsList");

Alternativas para validação

Restrições de validação relatam erros, mas não alteram o modelo.Se, em vez disso, você quiser impedir que o modelo se tornar inválida, você pode usar outras técnicas.

No entanto, essas técnicas não são recomendadas.Ele geralmente é melhor permitir que o usuário decidir como corrigir um modelo inválido.

Ajuste a alteração para restaurar o modelo de validade. Por exemplo, se o usuário define uma propriedade acima do máximo permitido, você pode redefinir a propriedade para o valor máximo.Para fazer isso, defina uma regra.Para obter mais informações, consulte Regras de propagam alterações dentro do modelo.

Reverta a transação se você tentar uma alteração inválida. Você também pode definir uma regra para essa finalidade, mas em alguns casos é possível substituir um manipulador de propriedade OnValueChanging(), ou para substituir um método, como OnDeleted(). para reverter uma transação, use this.Store.TransactionManager.CurrentTransaction.Rollback(). para obter mais informações, consulte Manipuladores de alteração de valor de propriedade de domínio.

Observação de cuidadoCuidado

Certifique-se de que o usuário sabe que a alteração foi ajustada ou revertida.Por exemplo, usarSystem.Windows.Forms.MessageBox.Show("message").

Consulte também

Conceitos

Navegando e atualizando um modelo de código de programa

Manipuladores de eventos propagam alterações fora do modelo