Validation dans un langage spécifique à un domaineValidation in a Domain-Specific Language

En tant qu'auteur d'un langage spécifique à un domaine (DSL), vous pouvez définir des contraintes de validation afin de vérifier que le modèle créé par l'utilisateur a un sens.As the author of a domain-specific language (DSL), you can define validation constraints to verify that the model created by the user is meaningful. Par exemple, si votre DSL permet aux utilisateurs de tracer l'arbre généalogique d'une famille et de ses ancêtres, vous pouvez écrire une contrainte qui garantit que les enfants ont des dates de naissance postérieures à celles de leurs parents.For example, if your DSL allows users to draw a family tree of people and their ancestors, you could write a constraint that ensures that children have birth dates after their parents.

Vous pouvez exécuter les contraintes de validation lorsque le modèle est enregistré, lorsqu’il est ouvert, et lorsque l’utilisateur exécute explicitement la Validate commande de menu.You can have the validation constraints execute when the model is saved, when it is opened, and when the user explicitly runs the Validate menu command. Vous pouvez aussi exécuter la validation sous le contrôle du programme.You can also execute validation under program control. Par exemple, vous pouvez exécuter la validation en réponse à la modification d'une valeur de propriété ou d'une relation.For example, you could execute validation in response to a change in a property value or relationship.

La validation est particulièrement importante si vous écrivez des modèles de texte ou d’autres outils qui traitent des modèles de vos utilisateurs.Validation is particularly important if you are writing text templates or other tools that process your users' models. La validation garantit que les modèles remplissent les conditions préalables assumées par ces outils.Validation ensures that the models fulfill the preconditions assumed by those tools.

Avertissement

Vous pouvez aussi autoriser que les contraintes de validation soient définies dans des extensions distinctes de votre DSL, en même temps que les gestionnaires de mouvements et les commandes de menu de l'extension.You can also allow validation constraints to be defined in separate extensions to your DSL, along with extension menu commands and gesture handlers. Les utilisateurs peuvent choisir d'installer ces extensions en plus de votre DSL.Users can choose to install these extensions in addition to your DSL. Pour plus d’informations, consultez étendre votre DSL à l’aide de MEF.For more information, see Extend your DSL by using MEF.

Exécution de la validationRunning Validation

Quand un utilisateur modifie un modèle, à savoir, une instance de votre langage spécifique à un domaine, les actions suivantes peuvent exécuter la validation :When a user is editing a model, that is, an instance of your domain-specific language, the following actions can run validation:

  • Cliquez sur le diagramme et sélectionnez valider tous les.Right-click the diagram and select Validate All.

  • Cliquez sur le nœud supérieur dans l’Explorateur de votre DSL, puis sélectionnez valider tous lesRight-click the top node in the Explorer of your DSL and select Validate All

  • Enregistrez le modèle.Save the model.

  • Ouvrez le modèle.Open the model.

  • En outre, vous pouvez écrire le code de programme qui exécute la validation comme partie d'une commande de menu ou en réponse à une modification, par exemple.In addition, you can write program code that runs validation, for example, as part of a menu command or in response to a change.

    Des erreurs de validation seront affiche dans le liste d’erreurs fenêtre.Any validation errors will appear in the Error List window. L'utilisateur peut double-cliquer sur un message d'erreur pour sélectionner les éléments du modèle qui sont à l'origine de l'erreur.The user can double-click an error message to select the model elements that are the cause of the error.

Définition des contraintes de validationDefining Validation Constraints

Vous définissez les contraintes de validation en ajoutant les méthodes de validation aux classes de domaine ou aux relations de votre DSL.You define validation constraints by adding validation methods to the domain classes or relationships of your DSL. Quand la validation est exécutée, que ce soit par l'utilisateur ou sous le contrôle du programme, tout ou partie des méthodes de validation est exécuté.When validation is run, either by the user or under program control, some or all of the validation methods are executed. Chaque méthode est appliquée à chaque instance de sa classe et il peut y avoir plusieurs méthodes de validation dans chaque classe.Each method is applied to each instance of its class, and there can be several validation methods in each class.

Chaque méthode de validation signale les erreurs éventuelles qu'elle détecte.Each validation method reports any errors that it finds.

Note

Les méthodes de validation signalent les erreurs, mais ne modifient pas le modèle.Validation methods report errors, but do not change the model. Si vous souhaitez ajuster ou empêchez certaines modifications, consultez Alternatives à la Validation.If you want to adjust or prevent certain changes, see Alternatives to Validation.

Pour définir une contrainte de validationTo define a validation constraint

  1. Activer la validation dans le Editor\Validation nœud :Enable validation in the Editor\Validation node:

    1. Ouvrez Dsl\DslDefinition.dsl.Open Dsl\DslDefinition.dsl.

    2. Dans l’Explorateur DSL, développez le éditeur nœud et sélectionnez Validation.In DSL Explorer, expand the Editor node and select Validation.

    3. Dans la fenêtre Propriétés, définissez la utilise propriétés true.In the Properties window, set the Uses properties to true. Il est plus pratique de définir toutes ces propriétés.It is most convenient to set all these properties.

    4. Cliquez sur transformer tous les modèles dans la barre d’outils de l’Explorateur de solutions.Click Transform All Templates in the Solution Explorer toolbar.

  2. Écrivez les définitions de classe partielle pour une ou plusieurs de vos classes de domaine ou relations de domaine.Write partial class definitions for one or more of your domain classes or domain relationships. Écrire ces définitions dans un fichier de code dans le Dsl projet.Write these definitions in a new code file in the Dsl project.

  3. Préfixez chaque classe avec l'attribut suivant :Prefix each class with this attribute:

    [ValidationState(ValidationState.Enabled)]  
    
    • Par défaut, cet attribut activera aussi la validation pour les classes dérivées.By default, this attribute will also enable validation for derived classes. Si vous voulez désactiver la validation pour une classe dérivée spécifique, vous pouvez utiliser ValidationState.Disabled.If you want to disable validation for a specific derived class, you can use ValidationState.Disabled.
  4. Ajoutez les méthodes de validation aux classes.Add validation methods to the classes. Chaque méthode de validation peut avoir un nom quelconque, mais un seul paramètre de type ValidationContext.Each validation method can have any name, but have one parameter of type ValidationContext.

    Elle doit être préfixée par un ou plusieurs attributs ValidationMethod :It must be prefixed with one or more ValidationMethod attributes:

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

    L'attribut ValidationCategories spécifie à quel moment la méthode est exécutée.The ValidationCategories specify when the method is executed.

    Par exemple :For example:

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

Notez les points suivants relatifs au code :Notice the following points about this code:

  • Vous pouvez ajouter des méthodes de validation aux classes de domaine ou relations de domaine.You can add validation methods to domain classes or domain relationships. Le code de ces types est dans Dsl\Generated Code\Domain*.cs.The code for these types is in Dsl\Generated Code\Domain*.cs.

  • Chaque méthode de validation s'applique à chaque instance de sa classe et de ses sous-classes.Each validation method is applied to every instance of its class and its subclasses. Dans le cas d'une relation de domaine, chaque instance est un lien entre deux éléments de modèle.In the case of a domain relationship, each instance is a link between two model elements.

  • Les méthodes de validation ne s'appliquent pas selon un ordre spécifié et chaque méthode ne s'applique pas aux instances de sa classe dans un ordre prévisible.Validation methods are not applied in any specified order, and each method is not applied to the instances of its class in any predictable order.

  • Il n'est généralement pas recommandé qu'une méthode de validation mette à jour le contenu du magasin, car cela conduirait à des résultats incohérents.It is usually bad practice for a validation method to update the store content, because this would lead to inconsistent results. La méthode doit à la place signaler une erreur en appelant context.LogError, LogWarning ou LogInfo.Instead, the method should report any error by calling context.LogError, LogWarning or LogInfo.

  • Dans l'appel de LogError, vous pouvez fournir une liste d'éléments de modèle ou de liens de relation qui seront sélectionnés quand l'utilisateur double-clique sur le message d'erreur.In the LogError call, you can provide a list of model elements or relationship links that will be selected when the user double-clicks the error message.

  • Pour plus d’informations sur la façon de lire le modèle de code de programme, consultez navigation et la mise à jour d’un modèle de Code de programme.For information about how to read the model in program code, see Navigating and Updating a Model in Program Code.

    L'exemple s'applique au modèle de domaine suivant.The example applies to the following domain model. La relation ParentsHaveChildren possède des rôles nommés Child et Parent.The ParentsHaveChildren relationship has roles that are named Child and Parent.

    Diagramme de définition DSL - modèle d’arbre généalogiqueDSL Definition diagram - family tree model

Catégories de validationValidation Categories

Dans l'attribut ValidationMethodAttribute, vous spécifiez à quel moment la méthode de validation doit s'exécuter.In the ValidationMethodAttribute attribute, you specify when the validation method should be executed.

CatégorieCategory ExécutionExecution
ValidationCategories Lorsque l'utilisateur appelle la commande de menu Valider.When the user invokes the Validate menu command.
ValidationCategories Lorsque le fichier de modèle est ouvert.When the model file is opened.
ValidationCategories Lorsque le fichier est enregistré.When the file is saved. S'il y a des erreurs de validation, l'utilisateur se voit offrir la possibilité d'annuler l'opération d'enregistrement.If there are validation errors, the user will be given the option of canceling the save operation.
ValidationCategories Lorsque le fichier est enregistré.When the file is saved. S'il y a des erreurs provenant des méthodes de la catégorie, l'utilisateur est prévenu qu'il peut ne pas être possible de rouvrir le fichier.If there are errors from methods in this category, the user is warned that it might not be possible to re-open the file.

Utilisez cette catégorie pour les méthodes de validation qui testent la présence de noms ou ID dupliqués, ou autres conditions susceptibles de produire des erreurs de chargement.Use this category for validation methods that test for duplicated names or IDs, or other conditions that might cause loading errors.
ValidationCategories Lorsque la méthode ValidateCustom est appelée.When the ValidateCustom method is called. Les validations de cette catégorie ne peuvent être appelées qu'à partir du code de programme.Validations in this category can be invoked only from program code.

Pour plus d’informations, consultez les catégories de Validation personnalisé.For more information, see Custom Validation Categories.

Où placer les méthodes de validationWhere to Place Validation Methods

Vous pouvez souvent obtenir le même effet en plaçant une méthode de validation sur un type différent.You can often achieve the same effect by placing a validation method on a different type. Par exemple, vous pouvez ajouter une méthode à la classe Person au lieu de la relation ParentsHaveChildren et procéder à son itération à travers les liens :For example, you could add a method to the Person class instead of the ParentsHaveChildren relationship, and have it iterate through the 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)  
        { ...  

Agrégation des contraintes de validation.Aggregating validation constraints. Pour appliquer la validation dans un ordre prédéfini, définissez une méthode de validation unique sur une classe propriétaire, telle l’élément racine de votre modèle.To apply validation in a predictable order, define a single validation method on an owner class, such the root element of your model. Cette technique permet aussi de regrouper plusieurs rapports d'erreur au sein d'un seul message.This technique also lets you aggregate multiple error reports into a single message.

L'inconvénient est que la méthode combinée est moins facile à gérer et que les contraintes doivent toutes avoir les mêmes ValidationCategories.Drawbacks are that the combined method is less easy to manage, and that the constraints must all have the same ValidationCategories. Il est donc recommandé que vous conserviez chaque contrainte dans une méthode distincte si possible.We therefore recommend that you keep each constraint in a separate method if possible.

Passage de valeurs dans le cache de contexte.Passing values in the context cache. Le paramètre de contexte a un dictionnaire dans lequel vous pouvez placer des valeurs arbitraires.The context parameter has a dictionary into which you can place arbitrary values. Le dictionnaire demeure pendant la durée de l'exécution de la validation.The dictionary persists for the life of the validation run. Une méthode de validation particulière peut, par exemple, conserver le nombre d'erreurs dans le contexte et l'utiliser pour éviter que la fenêtre d'erreurs ne déborde sous les messages répétés.A particular validation method could, for example, keep an error count in the context, and use it to avoid flooding the error window with repeated messages. Par exemple :For example:

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

Validation des multiplicitésValidation of Multiplicities

Les méthodes de validation pour vérifier la multiplicité minimale sont automatiquement générées pour votre DSL.Validation methods for checking minimum multiplicity are automatically generated for your DSL. Le code est écrit dans Dsl\Generated Code\MultiplicityValidation.cs.The code is written to Dsl\Generated Code\MultiplicityValidation.cs. Ces méthodes prennent effet lorsque vous activez la validation dans le Editor\Validation nœud dans l’Explorateur DSL.These methods take effect when you enable validation in the Editor\Validation node in DSL Explorer.

Si vous définissez la multiplicité d'un rôle d'une relation de domaine sur 1..* ou 1..1, mais que l'utilisateur ne crée pas le lien de cette relation, un message d'erreur de validation s'affiche.If you set the multiplicity of a role of a domain relationship to be 1..* or 1..1, but the user does not create a link of this relationship, a validation error message will appear.

Par exemple, si votre DSL a classes personne et ville et une relation PersonLivesInTown avec une relation 1..\* au niveau du rôle de la ville, puis pour chaque personne qui ne dispose d’aucune ville, un message d’erreur s’affiche.For example, if your DSL has classes Person and Town, and a relationship PersonLivesInTown with a relationship 1..\* at the Town role, then for each Person that has no Town, an error message will appear.

Exécution de la validation à partir du code de programmeRunning Validation from Program Code

Vous pouvez exécuter la validation en accédant à un ValidationController ou en en créant un.You can run validation by accessing or creating a ValidationController. Si vous souhaitez que les erreurs sont affichées à l’utilisateur dans la fenêtre d’erreur, utilisez le ValidationController qui est attaché à DocData de votre schéma.If you want the errors to be displayed to the user in the error window, use the ValidationController that is attached to your diagram's DocData. Par exemple, si vous écrivez une commande de menu, CurrentDocData.ValidationController est disponible dans la classe de l'ensemble de commandes :For example, if you are writing a menu command, CurrentDocData.ValidationController is available in the command set class:

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;   
...  

Pour plus d’informations, consultez Comment : ajouter une commande au Menu contextuel.For more information, see How to: Add a Command to the Shortcut Menu.

Vous pouvez aussi créer un contrôleur de validation distinct et gérer les erreurs vous-même.You can also create a separate validation controller, and manage the errors yourself. Par exemple :For example:

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) { ... }  
}  

Exécution de la validation quand une modification intervientRunning validation when a change occurs

Si vous voulez vous assurer que l'utilisateur est immédiatement averti si le modèle devient non valide, vous pouvez définir un événement de magasin qui exécute la validation.If you want to make sure that the user is warned immediately if the model becomes invalid, you can define a store event that runs validation. Pour plus d’informations sur les événements de la banque, consultez gestionnaires propager les modifications en dehors du modèle d’événement.For more information about store events, see Event Handlers Propagate Changes Outside the Model.

En plus du code de validation, ajoutez un fichier de code personnalisé dans votre DslPackage projet, avec un contenu similaire à l’exemple suivant.In addition to the validation code, add a custom code file to your DslPackage project, with content similar to the following example. Ce code utilise le ValidationController attaché au document.This code uses the ValidationController that is attached to the document. Ce contrôleur affiche les erreurs de validation dans la liste d'erreurs Visual StudioVisual Studio.This controller displays the validation errors in the Visual StudioVisual Studio error list.

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

Les gestionnaires sont aussi appelés après les opérations Annuler ou Rétablir qui affectent les liens ou les éléments.The handlers are also called after Undo or Redo operations that affect the links or elements.

Catégories de Validation personnaliséeCustom Validation Categories

En plus des catégories de validation standard, telles que Menu ou Ouvrir, vous pouvez définir vos propres catégories.In addition to the standard validation categories, such as Menu and Open, you can define your own categories. Vous pouvez invoquer ces catégories à partir du code de programme.You can invoke these categories from program code. L'utilisateur ne peut pas les appeler directement.The user cannot invoke them directly.

Une utilisation classique des catégories personnalisées consiste à définir une catégorie qui teste si le modèle satisfait aux conditions préalables d'un outil particulier.A typical use for custom categories is to define a category that tests whether the model satisfies the preconditions of a particular tool.

Pour ajouter une méthode de validation à une catégorie particulière, préfixez-la à l'aide d'un attribut comme suit :To add a validation method to a particular category, prefix it with an attribute like this:

[ValidationMethod(CustomCategory = "PreconditionsForGeneratePartsList")]  
[ValidationMethod(ValidationCategory.Menu)]   
private void TestForCircularLinks(ValidationContext context)   
{...}  

Note

Vous pouvez préfixer une méthode avec autant d'attributs [ValidationMethod()] que vous le souhaitez.You can prefix a method with as many [ValidationMethod()] attributes as you want. Vous pouvez ajouter une méthode aussi bien aux catégories personnalisées qu'aux catégories standard.You can add a method to both custom and standard categories.

Pour appeler une validation personnalisée :To invoke a custom validation:


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

Alternatives à la ValidationAlternatives to Validation

Les contraintes de validation signalent les erreurs, mais ne modifient pas le modèle.Validation constraints report errors, but do not change the model. Si, à la place, vous voulez empêcher que le modèle ne devienne non valide, vous pouvez utiliser d'autres techniques.If, instead, you want to prevent the model becoming invalid, you can use other techniques.

Cependant, ces techniques ne sont pas recommandées.However, these techniques are not recommended. Il est généralement préférable de laisser l'utilisateur décider de la façon dont un modèle non valide doit être corrigé.It is usually better to let the user decide how to correct an invalid model.

Ajustez la modification pour rétablir le modèle de validité.Adjust the change to restore the model to validity. Par exemple, si l’utilisateur définit une propriété au-dessus de la taille maximale autorisée, vous pourriez réinitialiser la propriété à la valeur maximale.For example, if the user sets a property above the allowed maximum, you could reset the property to the maximum value. Pour ce faire, définissez une règle.To do this, define a rule. Pour plus d’informations, consultez propager les modifications dans le modèle de règles.For more information, see Rules Propagate Changes Within the Model.

Restaurer la transaction si une modification non valide est tentée.Roll back the transaction if an invalid change is attempted. Vous pouvez également définir une règle à cet effet, mais dans certains cas, il est possible de remplacer un gestionnaire de propriété OnValueChanging(), ou pour remplacer une méthode comme OnDeleted(). pour restaurer une transaction, utilisez this.Store.TransactionManager.CurrentTransaction.Rollback(). pour plus d’informations plus d’informations, consultez gestionnaires de modification de valeur de propriété domaine.You could also define a rule for this purpose, but in some cases it is possible to override a property handler OnValueChanging(), or to override a method such as OnDeleted(). To roll back a transaction, use this.Store.TransactionManager.CurrentTransaction.Rollback(). For more information, see Domain Property Value Change Handlers.

Avertissement

Assurez-vous que l'utilisateur sache que la modification a été ajustée ou annulée.Make sure that the user knows that the change has been adjusted or rolled back. Par exemple, utilisez System.Windows.Forms.MessageBox.Show("message").For example, use System.Windows.Forms.MessageBox.Show("message").

Voir aussiSee Also

Navigation et la mise à jour d’un modèle de Code de programme Navigating and Updating a Model in Program Code
Propagation de modifications en dehors du modèle par des gestionnaires d’événementsEvent Handlers Propagate Changes Outside the Model