Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Como autor de uma linguagem específica de domínio (DSL), você pode definir restrições de validação para verificar se o modelo criado pelo usuário é significativo. Por exemplo, se sua DSL permite que os usuários desenhem uma árvore genealógica de pessoas e seus antepassados, você pode escrever uma restrição que garanta que as crianças tenham datas de nascimento depois de seus pais.
Você pode executar as restrições de validação quando o modelo é salvo, quando é aberto e quando o usuário executa explicitamente o comando de menu Validar . Você também pode executar a validação sob controle do programa. Por exemplo, você pode executar a validação em resposta a uma alteração em um valor de propriedade ou relacionamento.
A validação é particularmente importante se você estiver escrevendo modelos de texto ou outras ferramentas que processam os modelos dos usuários. A validação garante que os modelos cumprem as pré-condições assumidas por essas ferramentas.
Advertência
Você também pode permitir que restrições de validação sejam definidas em extensões separadas para sua DSL, juntamente com comandos de menu de extensão e manipuladores de gestos. Os usuários podem optar por instalar essas extensões além da sua DSL. Para obter mais informações, consulte Estender sua DSL usando MEF.
Executando a validação
Quando um usuário está editando um modelo, ou seja, uma instância do idioma específico do domínio, as seguintes ações podem executar a validação:
Clique com o botão direito do mouse no diagrama e selecione Validar tudo.
Clique com o botão direito do mouse no nó superior no Explorer da sua DSL e selecione Validar tudo
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 aparecerão na janela Lista de Erros . O usuário pode clicar duas vezes em uma mensagem de erro para selecionar os elementos do modelo que são a causa do erro.
Definindo restrições de validação
Você define restrições de validação adicionando métodos de validação às classes de domínio ou relacionamentos de sua DSL. Quando a validação é executada, pelo usuário ou sob controle do 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 quaisquer erros encontrados.
Observação
Os métodos de validação relatam erros, mas não alteram o modelo. Se quiser ajustar ou impedir determinadas alterações, consulte Alternativas à validação.
Para definir uma restrição de validação
Habilite a validação no nó Editor\Validação :
Abra Dsl\DslDefinition.dsl.
No DSL Explorer, expanda o nó Editor e selecione Validação.
Na janela Propriedades, defina as propriedades Uses como
true. É mais conveniente definir todas essas propriedades.Clique em Transformar Todos os Modelos na barra de ferramentas Gerenciador de Soluções.
Escreva definições parciais de classe para uma ou mais de suas classes de domínio ou relações de domínio. Escreva essas definições em um novo arquivo de código no projeto Dsl .
Prefixe cada classe com este atributo:
[ValidationState(ValidationState.Enabled)]- Por padrão, esse atributo também habilitará a validação para classes derivadas. Se quiser desativar a validação para uma classe derivada específica, você pode usar
ValidationState.Disabled.
- Por padrão, esse atributo também habilitará a validação para classes derivadas. Se quiser desativar a validação para uma classe derivada específica, você pode usar
Adicione métodos de validação às 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
ValidationMethodatributos:[ValidationMethod (ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu ) ]As ValidationCategories especificam quando o método é 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 a 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 link entre dois elementos do modelo.
Os métodos de validação não são aplicados em nenhuma ordem especificada e cada método não é aplicado às instâncias de sua classe em qualquer ordem previsível.
Geralmente, é uma má prática para um método de validação atualizar o conteúdo da loja, porque isso levaria a resultados inconsistentes. Em vez disso, o método deve relatar qualquer erro chamando
context.LogError,LogWarningouLogInfo.Na chamada LogError, você pode fornecer uma lista de elementos de modelo ou links de relacionamento que serão selecionados quando o usuário clicar duas vezes na mensagem de erro.
Para obter informações sobre como ler o modelo no código do programa, consulte Navegando e atualizando um modelo no código do programa.
O exemplo se aplica ao seguinte modelo de domínio. A relação ParentsHaveChildren tem papéis que são denominados Filho e Pai.
Categorias de validação
ValidationMethodAttribute No atributo , você especifica quando o método de validação deve ser executado.
| Categoria | Execution |
|---|---|
| ValidationCategories | Quando o usuário invoca o comando de menu Validar. |
| ValidationCategories | Quando o arquivo de modelo é aberto. |
| ValidationCategories | Quando o arquivo é salvo. Se houver erros de validação, o usuário terá a opção de cancelar a operação de salvamento. |
| ValidationCategories | Quando o arquivo é salvo. Se houver erros de métodos nesta categoria, o usuário é avisado de que pode não ser possível reabrir o arquivo. Use esta categoria para métodos de validação que testam nomes ou IDs duplicados ou outras condições que podem causar erros de carregamento. |
| ValidationCategories | Quando o método ValidateCustom é chamado. As validações nesta categoria só podem ser invocadas a partir do código do programa. Para obter mais informações, consulte Categorias de validação personalizadas. |
Onde colocar os métodos de validação
Muitas vezes, você pode obter o mesmo efeito colocando um método de validação em um tipo diferente. Por exemplo, você pode adicionar um método à classe Person em vez da relação ParentsHaveChildren e fazer com que ele itere através dos 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)
{ ...
Agregando restrições de validação. Para aplicar a validação em uma ordem previsível, defina um único método de validação 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 erros em uma única mensagem.
As desvantagens são que o método combinado é menos fácil de gerenciar e que as restrições devem ter todas as mesmas ValidationCategories. Portanto, recomendamos que você mantenha cada restrição em um método separado, se possível.
Passando valores na cache de contexto. O parâmetro context tem um dicionário no qual você pode colocar valores arbitrários. O dicionário persiste durante toda a vida útil do processo de validação. Um método de validação específico pode, por exemplo, manter uma contagem de erros no contexto e usá-la para evitar inundar a janela de erro 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
Os métodos de validação para verificar a multiplicidade mínima são gerados automaticamente para a sua DSL. O código é gravado em Dsl\Generated Code\MultiplicityValidation.cs. Esses métodos entram em vigor quando você habilita a validação no nó Editor\Validation no DSL Explorer.
Se você definir a multiplicidade de uma função de uma relação de domínio como 1..* ou 1..1, mas o usuário não criar um link dessa relação, uma mensagem de erro de validação aparecerá.
Por exemplo, se a sua DSL tiver as classes Pessoa e Cidade, e um relacionamento PessoaViveNaCidade com um relacionamento 1..\* no papel Cidade, então, para cada Pessoa que não tem uma Cidade, uma mensagem de erro aparecerá.
Executando a validação a partir do código do programa
Você pode executar a validação acessando ou criando um ValidationController. Se desejar que os erros sejam exibidos para o usuário na janela de erro, use o ValidationController anexado ao DocData do diagrama. Por exemplo, se você estiver escrevendo um comando de menu, CurrentDocData.ValidationController está disponível na classe do conjunto de comandos:
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 de validação separado 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 a validação quando ocorre uma alteração
Se quiser garantir que o usuário seja avisado imediatamente se o modelo se tornar inválido, você pode definir um evento de armazenamento que execute 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, adicione um arquivo de código personalizado ao seu projeto DslPackage , com conteúdo semelhante ao exemplo a seguir. Este código usa o ValidationController que está anexado ao documento. Este controlador exibe os erros de validação na lista de erros do Visual Studio.
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 também são chamados após operações de Desfazer ou Refazer que afetam os links ou elementos.
Categorias de validação personalizadas
Além das categorias de validação padrão, como Menu e Abrir, você pode definir suas próprias categorias. Você pode invocar 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 ferramenta específica.
Para adicionar um método de validação a uma categoria específica, prefixe-o com um atributo como este:
[ValidationMethod(CustomCategory = "PreconditionsForGeneratePartsList")]
[ValidationMethod(ValidationCategory.Menu)]
private void TestForCircularLinks(ValidationContext context)
{...}
Observação
Você pode prefixar um método com quantos [ValidationMethod()] atributos desejar. Você pode adicionar um método às categorias padrão e personalizadas.
Para invocar uma validação personalizada:
// Invoke all validation methods in a custom category:
validationController.ValidateCustom
(store, // or a list of model elements
"PreconditionsForGeneratePartsList");
Alternativas à validação
As restrições de validação relatam erros, mas não alteram o modelo. Se, em vez disso, você quiser evitar que o modelo se torne inválido, você pode usar outras técnicas.
No entanto, estas técnicas não são recomendadas. Normalmente, é melhor deixar o usuário decidir como corrigir um modelo inválido.
Ajuste a alteração para restaurar a validade do modelo. Por exemplo, se o usuário definir uma propriedade acima do máximo permitido, você poderá redefini-la para o valor máximo. Para fazer isso, defina uma regra. Para obter mais informações, consulte Regras propagam alterações dentro do modelo.
Reverta a transação se uma alteração inválida for realizada. Você também pode definir uma regra para essa finalidade, mas em alguns casos é possível substituir um manipulador de propriedade OnValueChanging() ou 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.
Advertência
Certifique-se de que o usuário sabe que a alteração foi ajustada ou revertida. Por exemplo, use System.Windows.Forms.MessageBox.Show("message").