La validación en los lenguajes específicos de dominioValidation in a Domain-Specific Language

Como autor de un lenguaje específico de dominio (DSL), puede definir restricciones de validación para comprobar que el modelo creado por el usuario tiene significado.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. Por ejemplo, si su DSL permite a los usuarios dibujar un árbol genealógico de personas y antepasados, podría escribir una restricción que garantice que la fecha de nacimiento de los hijos sea posterior a la de los padres.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.

Puede tener las restricciones de validación que ejecutará cuando se guarda el modelo, cuando se abre, y cuando el usuario ejecute explícitamente el validar comando de menú.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. También puede ejecutar la validación bajo el control del programa.You can also execute validation under program control. Por ejemplo, podría ejecutar la validación en respuesta a un cambio en un valor de propiedad o de una relación.For example, you could execute validation in response to a change in a property value or relationship.

La validación es especialmente importante si va a escribir plantillas de texto u otras herramientas que procesan los modelos de los usuarios.Validation is particularly important if you are writing text templates or other tools that process your users' models. La validación garantiza que los modelos cumplen las condiciones previas que esas herramientas dan por hecho.Validation ensures that the models fulfill the preconditions assumed by those tools.

Warning

También puede permitir que las restricciones de validación se definan en extensiones diferentes de su DSL, junto con los controladores de gestos y los comandos de menú de la extensión.You can also allow validation constraints to be defined in separate extensions to your DSL, along with extension menu commands and gesture handlers. Los usuarios pueden elegir instalar estas extensiones además de su DSL.Users can choose to install these extensions in addition to your DSL. Para obtener más información, consulte ampliar DSL mediante MEF.For more information, see Extend your DSL by using MEF.

Ejecutar la validaciónRunning Validation

Cuando un usuario edita un modelo, es decir, una instancia de su lenguaje específico de dominio, las siguientes acciones pueden ejecutar la validación:When a user is editing a model, that is, an instance of your domain-specific language, the following actions can run validation:

  • Haga clic en el diagrama y seleccione validar todos los.Right-click the diagram and select Validate All.

  • Haga clic en el nodo superior en el Explorador de su DSL y seleccionar validar todos losRight-click the top node in the Explorer of your DSL and select Validate All

  • Guardar el modelo.Save the model.

  • Abrir el modelo.Open the model.

  • Además, puede escribir código de programa que ejecute la validación, por ejemplo, como parte de un comando de menú o en respuesta a un cambio.In addition, you can write program code that runs validation, for example, as part of a menu command or in response to a change.

    Errores de validación aparecerán en el lista de errores ventana.Any validation errors will appear in the Error List window. El usuario puede hacer doble clic en un mensaje de error para seleccionar los elementos del modelo que causaron el error.The user can double-click an error message to select the model elements that are the cause of the error.

Definir restricciones de validaciónDefining Validation Constraints

Para definir restricciones de validación, se agregan métodos de validación a las relaciones o clases de dominio de su DSL.You define validation constraints by adding validation methods to the domain classes or relationships of your DSL. Cuando el usuario o el control del programa ejecutan la validación, se ejecutan algunos o todos los métodos de validación.When validation is run, either by the user or under program control, some or all of the validation methods are executed. Cada método se aplica a cada una de las instancias de la clase, y puede haber varios métodos de validación en cada clase.Each method is applied to each instance of its class, and there can be several validation methods in each class.

Cada método de validación notifica todos los errores que encuentra.Each validation method reports any errors that it finds.

Note

Los métodos de validación notifican los errores, pero no cambian el modelo.Validation methods report errors, but do not change the model. Si desea ajustar o evitar determinados cambios, consulte alternativas a la validación.If you want to adjust or prevent certain changes, see Alternatives to Validation.

Para definir una restricción de validaciónTo define a validation constraint

  1. Habilite la validación en el Editor\Validation nodo:Enable validation in the Editor\Validation node:

    1. Abra Dsl\DslDefinition.dsl.Open Dsl\DslDefinition.dsl.

    2. En el Explorador de DSL, expanda el Editor nodo y seleccione validación.In DSL Explorer, expand the Editor node and select Validation.

    3. En la ventana Propiedades, establezca la usa propiedades a true.In the Properties window, set the Uses properties to true. Lo más conveniente es establecer todas las propiedades.It is most convenient to set all these properties.

    4. Haga clic en Transformar todas las plantillas en el el Explorador de soluciones barra de herramientas.Click Transform All Templates in the Solution Explorer toolbar.

  2. Escriba definiciones de clases parciales para una o varias de sus clases de dominio o relaciones de dominio.Write partial class definitions for one or more of your domain classes or domain relationships. Escriba estas definiciones en un nuevo archivo de código en el Dsl proyecto.Write these definitions in a new code file in the Dsl project.

  3. Asigne este atributo como prefijo a cada clase:Prefix each class with this attribute:

    [ValidationState(ValidationState.Enabled)]
    
    • De forma predeterminada, este atributo también permitirá validar clases derivadas.By default, this attribute will also enable validation for derived classes. Si quiere deshabilitar la validación para una clase derivada específica, puede usar ValidationState.Disabled.If you want to disable validation for a specific derived class, you can use ValidationState.Disabled.
  4. Agregue métodos de validación a las clases.Add validation methods to the classes. Los métodos de validación pueden tener cualquier nombre, pero tienen que tener un parámetro del tipo ValidationContext.Each validation method can have any name, but have one parameter of type ValidationContext.

    Deben tener como prefijo uno o varios atributos ValidationMethod:It must be prefixed with one or more ValidationMethod attributes:

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

    ValidationCategories especifica cuándo se ejecuta el método.The ValidationCategories specify when the method is executed.

    Por ejemplo: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);
    }
  }

Observe los siguientes aspectos sobre este código:Notice the following points about this code:

  • Puede agregar métodos de validación a las clases de dominio o a las relaciones de dominio.You can add validation methods to domain classes or domain relationships. El código para estos tipos está en Dsl\Generated Code\Domain*.cs.The code for these types is in Dsl\Generated Code\Domain*.cs.

  • Cada método de validación se aplica a todas las instancias de su clase y sus subclases.Each validation method is applied to every instance of its class and its subclasses. En el caso de una relación de dominio, cada instancia es un vínculo entre dos elementos de modelo.In the case of a domain relationship, each instance is a link between two model elements.

  • Los métodos de validación no se aplican con un orden específico, y los métodos no se aplican a las instancias de su clase con ningún orden predecible.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.

  • Normalmente no es recomendable que un método de validación actualice el contenido del almacén, porque podría producir resultados incoherentes.It is usually bad practice for a validation method to update the store content, because this would lead to inconsistent results. En su lugar, el método debe notificar los errores llamando a context.LogError, LogWarning o LogInfo.Instead, the method should report any error by calling context.LogError, LogWarning or LogInfo.

  • En la llamada a LogError, puede proporcionar una lista de elementos de modelo o de vínculos de relación que se seleccionarán cuando el usuario haga doble clic en el mensaje de error.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.

  • Para obtener información acerca de cómo leer el modelo en el código de programa, consulte navegar y actualizar un modelo en el código de programa.For information about how to read the model in program code, see Navigating and Updating a Model in Program Code.

    El ejemplo se aplica al siguiente modelo de dominio.The example applies to the following domain model. La relación ParentsHaveChildren tiene roles con los nombres Child y Parent.The ParentsHaveChildren relationship has roles that are named Child and Parent.

    Diagrama de definición de DSL - modelo de árbol genealógico

Categorías de validaciónValidation Categories

En el atributo ValidationMethodAttribute se especifica cuándo se debe ejecutar el método de validación.In the ValidationMethodAttribute attribute, you specify when the validation method should be executed.

CategoríaCategory ExecutionExecution
ValidationCategories Cuando el usuario invoca el comando de menú Validate (Validar).When the user invokes the Validate menu command.
ValidationCategories Cuando se abre el archivo del modelo.When the model file is opened.
ValidationCategories Cuando se guarda el archivo.When the file is saved. Si hay errores de validación, el usuario tendrá la opción de cancelar la operación de guardado.If there are validation errors, the user will be given the option of canceling the save operation.
ValidationCategories Cuando se guarda el archivo.When the file is saved. Si hay errores de métodos en esta categoría, se advierte al usuario de que quizás no sea posible volver a abrir el archivo.If there are errors from methods in this category, the user is warned that it might not be possible to re-open the file.

Use esta categoría para los métodos de validación que comprueban si hay nombres o identificadores duplicados, u otras condiciones que pudieran provocar errores de carga.Use this category for validation methods that test for duplicated names or IDs, or other conditions that might cause loading errors.
ValidationCategories Cuando se llama al método ValidateCustom.When the ValidateCustom method is called. Las validaciones de esta categoría se pueden invocar solo desde el código de programa.Validations in this category can be invoked only from program code.

Para obtener más información, consulte categorías de validación personalizadas.For more information, see Custom Validation Categories.

Dónde colocar los métodos de validaciónWhere to Place Validation Methods

Con frecuencia se puede lograr el mismo efecto colocando un método de validación en un tipo diferente.You can often achieve the same effect by placing a validation method on a different type. Por ejemplo, podría agregar un método a la clase Person en lugar de a la relación ParentsHaveChildren, y hacer que itere por los vínculos: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)
        { ...

Agregar restricciones de validación.Aggregating validation constraints. Para aplicar la validación en un orden predecible, defina un método de validación única en una clase de propietario, como el elemento raíz del modelo.To apply validation in a predictable order, define a single validation method on an owner class, such the root element of your model. Esta técnica también perite agregar varios informes de error en un único mensaje.This technique also lets you aggregate multiple error reports into a single message.

Las desventajas son que el método combinado es menos fácil de administrar y que todas las restricciones deben tener las mismas ValidationCategories.Drawbacks are that the combined method is less easy to manage, and that the constraints must all have the same ValidationCategories. Por lo tanto, le recomendamos que mantenga cada restricción en un método diferente, si es posible.We therefore recommend that you keep each constraint in a separate method if possible.

Pasar valores en la memoria caché del contexto.Passing values in the context cache. El parámetro de contexto tiene un diccionario que se pueden colocar valores arbitrarios.The context parameter has a dictionary into which you can place arbitrary values. El diccionario persiste mientras dura la ejecución de la validación.The dictionary persists for the life of the validation run. Un método de validación determinado podría, por ejemplo, mantener un recuento de errores en el contexto y usarlo para evitar desbordar la ventana de errores con mensajes repetidos.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. Por ejemplo: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( ... ); }

Validación de multiplicidadesValidation of Multiplicities

Su DSL genera automáticamente los métodos de validación para comprobar la multiplicidad mínima.Validation methods for checking minimum multiplicity are automatically generated for your DSL. El código se escribe en Dsl\Generated Code\MultiplicityValidation.cs.The code is written to Dsl\Generated Code\MultiplicityValidation.cs. Estos métodos surten efecto cuando se habilita la validación en el Editor\Validation nodo en el Explorador de DSL.These methods take effect when you enable validation in the Editor\Validation node in DSL Explorer.

Si establece que la multiplicidad de un rol de una relación de dominio es 1..* o 1..1, pero el usuario no crea un vínculo de esta relación, aparecerá un mensaje de error de validación.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.

Por ejemplo, si su DSL tiene clases Person y Town y una relación PersonLivesInTown con una relación 1..\ * en el rol Town, a continuación, para cada persona que no tenga ningún Town, un mensaje de error aparecerá.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.

Ejecutar la validación desde el código de programaRunning Validation from Program Code

Para ejecutar la validación, puede acceder a un controlador de validación o crear uno.You can run validation by accessing or creating a ValidationController. Si desea que los errores que se mostrará al usuario en la ventana de error, utilice el controlador de validación que está asociado al DocData de su diagrama.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. Por ejemplo, si está escribiendo un comando de menú, CurrentDocData.ValidationController está disponible en la clase de conjunto de comandos: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;
...

Para obtener más información, consulte Cómo: agregar un comando al menú contextual.For more information, see How to: Add a Command to the Shortcut Menu.

También puede crear un controlador de validación diferente y administrar los errores por sí mismo.You can also create a separate validation controller, and manage the errors yourself. Por ejemplo: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) { ... }
}

Ejecutar la validación cuando se produce un cambioRunning validation when a change occurs

Si quiere asegurarse de que el usuario reciba una advertencia inmediata si el modelo queda invalidado, puede definir un evento de almacén que ejecute la validación.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. Para obtener más información acerca de los eventos de almacén, vea controladores propagar los cambios fuera el modelo de evento.For more information about store events, see Event Handlers Propagate Changes Outside the Model.

Además del código de validación, agregue un archivo de código personalizado a su DslPackage proyecto con un contenido similar al ejemplo siguiente.In addition to the validation code, add a custom code file to your DslPackage project, with content similar to the following example. Este código usa el ValidationController que está asociado al documento.This code uses the ValidationController that is attached to the document. Este controlador muestra los errores de validación en la lista de errores de Visual Studio.This controller displays the validation errors in the Visual 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);
    }
  }
}

También se llama a los controladores después de las operaciones de deshacer o rehacer que afectan a los vínculos o elementos.The handlers are also called after Undo or Redo operations that affect the links or elements.

Categorías personalizadas de validaciónCustom Validation Categories

Además de las categorías de validación estándar, como Menu y Open, puede definir sus propias categorías.In addition to the standard validation categories, such as Menu and Open, you can define your own categories. Puede invocar estas categorías desde el código de programa.You can invoke these categories from program code. El usuario no puede invocarlas directamente.The user cannot invoke them directly.

Un uso típico de las categorías personalizadas es definir una categoría que pruebe si el modelo cumple las condiciones previas de una herramienta determinada.A typical use for custom categories is to define a category that tests whether the model satisfies the preconditions of a particular tool.

Para agregar un método de validación a una categoría determinada, asígnele como prefijo un atributo como este: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

A un método se pueden asignar como prefijo tantos atributos [ValidationMethod()] como quiera.You can prefix a method with as many [ValidationMethod()] attributes as you want. Puede agregar un método a las categorías tanto personalizadas como estándar.You can add a method to both custom and standard categories.

Para invocar una validación personalizada:To invoke a custom validation:


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

Alternativas a la validaciónAlternatives to Validation

Las restricciones de validación notifican los errores, pero no cambian el modelo.Validation constraints report errors, but do not change the model. Si, en su lugar, quiere evitar que el modelo quede invalidado, puede usar otras técnicas.If, instead, you want to prevent the model becoming invalid, you can use other techniques.

Sin embargo, estas técnicas no se recomiendan.However, these techniques are not recommended. Normalmente lo mejor es dejar que el usuario decida cómo corregir un modelo no válido.It is usually better to let the user decide how to correct an invalid model.

Ajuste el cambio para restaurar el modelo de validez.Adjust the change to restore the model to validity. Por ejemplo, si el usuario establece una propiedad por encima del máximo permitido, se pudo restablecer la propiedad al valor máximo.For example, if the user sets a property above the allowed maximum, you could reset the property to the maximum value. Para ello, defina una regla.To do this, define a rule. Para obtener más información, consulte propagar cambios en el modelo de reglas de.For more information, see Rules Propagate Changes Within the Model.

Revertir la transacción si se intenta realizar un cambio no válido.Roll back the transaction if an invalid change is attempted. También podría definir una regla para este propósito, pero en algunos casos es posible reemplazar un controlador de propiedad OnValueChanging(), o para invalidar un método como OnDeleted(). para revertir una transacción, use this.Store.TransactionManager.CurrentTransaction.Rollback(). para obtener más información obtener información, consulte controladores de cambio de valor de propiedad de dominio.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.

Warning

Asegúrese de que el usuario sabe que el cambio se ha ajustado o revertido.Make sure that the user knows that the change has been adjusted or rolled back. Por ejemplo, use System.Windows.Forms.MessageBox.Show("message")..For example, use System.Windows.Forms.MessageBox.Show("message").

Vea tambiénSee Also