Partager via


Validation dans un langage spécifique à un domaine

En tant qu'auteur d'un langage spécifique au domaine, (DSL) vous pouvez définir des contraintes de validation pour vérifier que le modèle créé par l'utilisateur est pertinent.Par exemple, si votre DÉSOLÉ permet aux utilisateurs de dessiner un arbre généalogique des personnes et les ancêtres, vous pouvez écrire une contrainte qui garantit que les enfants ont des dates de naissance après leurs parents.

Vous pouvez avoir des contraintes de validation lorsque le modèle est stocké, lorsqu'il est ouvert et, lorsque l'utilisateur exécute explicitement la commande de menu de Valider .Vous pouvez également effectuer la validation sous le contrôle du programme.Par exemple, vous pouvez exécuter la validation en réponse à une modification de la valeur d'une propriété ou d'une relation.

La validation est particulièrement importante si vous écrivez des modèles de texte ou d'autres outils qui fonctionnent sur des modèles de vos utilisateurs.La validation garantit que les modèles remplissent les conditions préalables assumées par ces outils.

Mise en gardeAttention

Vous pouvez également autoriser les contraintes de validation à définir dans les extensions séparées à votre DÉSOLÉ, ainsi que des commandes de menu d'extension et les gestionnaires de mouvements.Les utilisateurs peuvent choisir d'installer ces extensions en plus de votre DÉSOLÉ.Pour plus d'informations, consultez Extension de votre DSL à l'aide de MEF.

Validation en cours de exécution

Lorsqu'un utilisateur modifie un modèle, c. autrement dit., une instance de votre langage spécifique au domaine, les actions suivantes peuvent effectuer la validation :

  • Cliquez avec le bouton droit sur le diagramme et sélectionnez Validez toutes.

  • Cliquez avec le bouton droit sur le nœud supérieur dans l'explorateur de votre DÉSOLÉ et sélectionnez Validez toutes

  • Enregistrez le modèle.

  • ouvrez le modèle.

  • En outre, vous pouvez écrire le code du programme qui effectue la validation, par exemple, dans le cadre d'une commande de menu ou en réponse à une modification.

Toutes les erreurs de validation s'affichent dans la fenêtre de Liste d'erreurs .L'utilisateur peut double-cliquer sur un message d'erreur pour sélectionner les éléments de modèle qui sont la cause de l'erreur.

définir des contraintes de validation

Vous définissez des contraintes de validation en ajoutant des méthodes de validation aux classes de domaine ou aux relations de votre DÉSOLÉ.Lorsque la validation est exécutée, par l'utilisateur ou sous le contrôle du programme, certaines ou toutes les méthodes de validation sont exécutés.Chaque méthode est appliquée à chaque instance de sa classe, et il peut exister plusieurs méthodes de validation dans chaque classe.

Chaque méthode de validation signale toutes les erreurs qu'elle trouve.

[!REMARQUE]

Les méthodes de validation indiquent des erreurs, mais ne modifient pas le modèle.Si vous souhaitez ajuster ou entraver certaines modifications, consultez l' Alternatives à la validation.

pour définir une contrainte de validation

  1. Vérifiez la validation dans le nœud de Editor\Validation :

    1. Ouvrez Dsl\DslDefinition.dsl.

    2. Dans l'explorateur DÉSOLÉ, développez le nœud d' Éditeur et sélectionnez Validation.

    3. dans la fenêtre Propriétés, définissez les propriétés d' Utilise à true.Il est plus pratique de définir toutes ces propriétés.

    4. Cliquez sur Transformer tous les modèles dans la barre d'outils explorateur de solutions.

  2. Entrez les définitions de classe partielle pour un ou plusieurs de vos classes de domaine ou relations de domaine.Entrez ces définitions dans un nouveau fichier de code du projet d' Dsl .

  3. Ajoutez un préfixe à chaque classe avec cet attribut :

    [ValidationState(ValidationState.Enabled)]
    
    • Par défaut, cet attribut permet également la validation pour les classes dérivées.si vous souhaitez désactiver la validation pour une classe dérivée spécifique, vous pouvez utiliser ValidationState.Disabled.
  4. ajoutez les méthodes de validation aux classes.chaque méthode de validation peut avoir n'importe quel nom, mais a un paramètre de type ValidationContext.

    Il doit être précédé du préfixe un ou plusieurs attributs d' ValidationMethod :

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

    Le ValidationCategories spécifient lorsque la méthode est exécutée.

Par exemple :

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 à propos de ce code :

  • vous pouvez ajouter des méthodes de validation aux classes de domaine ou aux relations de domaine.Le code pour ces types se trouve dans Dsl\Generated Code\Domain*.cs.

  • chaque méthode de validation est appliquée à chaque instance de sa classe et de ses sous-classes.Dans le cas d'une relation de domaine, chaque instance est un lien entre deux éléments de modèle.

  • Les méthodes de validation ne sont appliquées dans une commande spécifiée, et chaque méthode n'est pas appliquée aux instances de sa classe dans un ordre précis prévisible.

  • Il est recommandé de généralement incorrect pratique pour une méthode de validation afin de mettre à jour le contenu du magasin, car cela mènerait des résultats incohérents.À la place, la méthode doit enregistrer toute erreur en appelant context.LogError, LogWarning ou 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 lorsque l'utilisateur double-clique sur le message d'erreur.

  • Pour plus d'informations sur la lecture le modèle dans le code du programme, consultez Navigation et mise à jour d'un modèle dans le code de programme.

l'exemple applique au modèle suivant de domaine.La relation de ParentsHaveChildren a des rôles qui sont nommés Child et parent.

Diagramme de définition DSL - modèle d'arbre généalogique

catégories de validation

Dans l'attribut d' ValidationMethod , vous spécifiez lorsque la méthode de validation doit être exécutée.

Catégorie

Exécution

Menu

Lorsque l'utilisateur appelle la commande de menu valider.

Open

Lorsque le fichier modèle est ouvert.

Save

Lors de l'enregistrement du fichier.S'il y a des erreurs de validation, l'utilisateur sera donné la possibilité d'annuler l'opération d'enregistrement.

Load

Lors de l'enregistrement du fichier.S'il y a des erreurs des méthodes dans cette catégorie, l'utilisateur est prévenu qu'il ne peut pas être possible de rouvrir le fichier.

Utilisez cette catégorie pour les méthodes de validation qui déterminent les noms ou les identificateurs dupliqués, ou d'autres conditions susceptibles de provoquer des erreurs de chargement.

Custom

Lorsque la méthode de ValidateCustom est appelée.Les rôles dans cette catégorie peuvent être appelées seulement le code du programme.

Pour plus d'informations, consultez Catégories de validation personnalisées.

L'emplacement des méthodes de validation

Vous pouvez souvent parvenir au même résultat en plaçant une méthode de validation sur un autre type.Par exemple, vous pouvez ajouter une méthode à la classe de personnes au lieu de la relation de ParentsHaveChildren, et la configurer pour itérer au sein de les liens :

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

Regrouper les contraintes de validation. Pour appliquer la validation dans une commande prévisible, définissez une seule méthode de validation sur une classe propriétaire, telle l'élément racine de votre modèle.Cette technique permet également de regrouper plusieurs rapports d'erreurs dans un message unique.

Les inconvénients sont qu'il est moins facile à gérer la méthode combinées, et que les contraintes doivent tous avoir même ValidationCategories.Nous vous recommandons donc que vous conservez chaque contrainte dans une méthode distincte si possible.

Passage de valeurs dans le cache de contexte. le paramètre de contexte a un dictionnaire dans lequel vous pouvez placer des valeurs arbitraires.Le dictionnaire est conservée pour la vie du test de validation.Une méthode particulière de validation peut, par exemple, conserver un nombre d'erreur dans le contexte, et l'utiliser pour éviter d'inonder la fenêtre d'erreur avec les messages répétés.Par exemple :

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és

Les méthodes de validation pour activer la multiplicité minimum sont générées automatiquement pour votre DÉSOLÉ.Le code est écrit dans Dsl\Generated Code\MultiplicityValidation.cs.Ces méthodes sont appliquées lorsque vous activez la validation dans le nœud d' éditeur \Validation dans l'explorateur DÉSOLÉ.

Si vous définissez la multiplicité d'un rôle d'une relation de domaine à être 1. */ou 1..1, mais l'utilisateur ne crée pas de lien de cette relation, un message d'erreur de validation s'affiche.

Par exemple, si votre DÉSOLÉ est la personne et la ville de classes, et une relation PersonLivesInTown avec une relation 1..* au rôle de ville, puis pour chaque personne qui n'a pas de ville, un message d'erreur s'affiche.

Validation en cours de exécution du code de programme

vous pouvez exécuter la validation en accédant ou en créant à un ValidationController.Si vous souhaitez les erreurs à afficher à l'utilisateur dans la fenêtre d'erreur, utilisez le ValidationController associé à DocData de votre schéma.Par exemple, si vous entrez une commande de menu, CurrentDocData.ValidationController est disponible dans la classe de jeu de commandes :

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.

Vous pouvez également créer un contrôleur distinct de validation, et gérer les erreurs vous-même.Par exemple :

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

Validation en cours de exécution lorsqu'une modification est apportée

Si vous souhaitez vous assurer que l'utilisateur est prévenu immédiatement si le modèle est plus valide, vous pouvez définir un événement de le magasin qui effectue la validation.Pour plus d'informations sur les événements de le magasin, consultez Propagation de modifications en dehors du modèle par des gestionnaires d'événements.

En plus de le code de validation, ajoutez un fichier de code personnalisé à votre projet d' DslPackage , avec le contenu semblable à l'exemple suivant.Ce code utilise ValidationController joint au document.ce contrôleur affiche les erreurs de validation dans la liste d'erreurs de 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);
    }
  }
} 

Les gestionnaires sont également appelés après rétablissement des opérations d'annulation ou de rétablissement qui affectent des liens ou afficher des éléments.

Catégories de validation personnalisées

En plus de les catégories standard de validation, telles que le menu et ouvrez, vous pouvez définir vos propres catégories.vous pouvez appeler ces catégories de code du programme.l'utilisateur ne peut pas les appeler directement.

Une utilisation courante pour les catégories personnalisées est de définir une catégorie qui teste si le modèle respectent les conditions préalables d'un outil particulier.

Pour ajouter une méthode de validation à une catégorie particulière, préfixez-la avec un attribut comme suit :

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

[!REMARQUE]

Vous pouvez ajouter une méthode avec autant d'attributs d' [ValidationMethod()] que vous le souhaitez.Vous pouvez ajouter une méthode à des catégories personnalisées et standard.

pour appeler une validation personnalisée :

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

Alternatives à la validation

Les contraintes de validation indiquent des erreurs, mais ne modifient pas le modèle.Si, à la place, vous souhaitez empêcher le modèle devenir non valide, vous pouvez utiliser d'autres techniques.

Toutefois, ces techniques ne sont pas recommandées.Il est généralement préférable de laisser l'utilisateur décider comment corriger un type valide.

Ajustez la modification pour restaurer le modèle à la validité. Par exemple, si l'utilisateur définit une propriété au-dessus de le maximale autorisée, vous pouvez redéfinir la propriété à la valeur maximale.Pour ce faire, définissez une règle.Pour plus d'informations, consultez Propagation de modifications dans le modèle par des règles.

Restaurer la transaction si une modification non valide est tentée. Vous pouvez également définir une règle à cet effet, mais dans certains cas il est possible de modifier un gestionnaire **OnValueChanging()**de propriété, ou pour substituer une méthode telle qu' OnDeleted(). pour restaurer une transaction, l'utilisation this.Store.TransactionManager.CurrentTransaction.Rollback(). pour plus d'informations, consultez Gestionnaire de modification de la valeur de propriété du domaine.

Mise en gardeAttention

Vérifiez que l'utilisateur sait que la modification a été ajustée ou restaurée.Par exemple, utilisez System.Windows.Forms.MessageBox.Show("message").

Voir aussi

Concepts

Navigation et mise à jour d'un modèle dans le code de programme

Propagation de modifications en dehors du modèle par des gestionnaires d'événements