Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Come autore di un linguaggio specifico del dominio (DSL) è possibile definire vincoli di convalida per verificare che il modello creato dall'utente sia significativo. Ad esempio, se il linguaggio DSL consente agli utenti di disegnare un albero di famiglia di persone e dei loro antenati, è possibile scrivere un vincolo che garantisce che i bambini abbiano date di nascita dopo i genitori.
È possibile che i vincoli di convalida vengano eseguiti quando il modello viene salvato, quando viene aperto e quando l'utente esegue in modo esplicito il comando di menu Convalida . È anche possibile eseguire la convalida sotto il controllo del programma. Ad esempio, è possibile eseguire la convalida in risposta a una modifica in un valore o una relazione di proprietà.
La convalida è particolarmente importante se si scrivono modelli di testo o altri strumenti che elaborano i modelli degli utenti. La convalida garantisce che i modelli soddisfino le precondizioni considerate da tali strumenti.
Avvertimento
È anche possibile consentire la definizione dei vincoli di convalida in estensioni separate del tuo DSL, insieme ai comandi di menu dell'estensione e ai gestori dei gesti. Gli utenti possono scegliere di installare queste estensioni oltre al DSL. Per ulteriori informazioni, vedere Estendere DSL tramite MEF.
Esecuzione della convalida
Quando un utente sta modificando un modello, ovvero un'istanza del linguaggio specifico del dominio, le azioni seguenti possono eseguire la convalida:
Fare clic con il pulsante destro del mouse sul diagramma e scegliere Convalida tutto.
Fare clic con il pulsante destro del mouse sul nodo superiore in Explorer del linguaggio DSL e scegliere Convalida tutto
Salva il modello.
Aprire il modello.
Inoltre, è possibile scrivere codice programma che esegue la convalida, ad esempio come parte di un comando di menu o in risposta a una modifica.
Eventuali errori di convalida verranno visualizzati nella finestra Elenco errori . L'utente può fare doppio clic su un messaggio di errore per selezionare gli elementi del modello che sono la causa dell'errore.
Definizione dei vincoli di convalida
È possibile definire i vincoli di convalida aggiungendo metodi di convalida alle classi di dominio o alle relazioni del linguaggio DSL. Quando viene eseguita la convalida, dall'utente o dal controllo del programma, vengono eseguiti alcuni o tutti i metodi di convalida. Ogni metodo viene applicato a ogni istanza della relativa classe e in ogni classe possono essere presenti diversi metodi di convalida.
Ogni metodo di convalida segnala eventuali errori rilevati.
Annotazioni
I metodi di convalida segnalano errori, ma non modificano il modello. Per modificare o impedire determinate modifiche, vedere Alternative alla convalida.
Per definire un vincolo di convalida
Abilitare la convalida nel nodo Editor\Validation :
Aprire Dsl\DslDefinition.dsl.
In Esplora DSL espandere il nodo Editor e selezionare Convalida.
Nella finestra Proprietà impostare le proprietà Uses su
true. È più comodo impostare tutte queste proprietà.Fare clic su Trasforma tutti i modelli nella barra degli strumenti di Esplora soluzioni .
Scrivere definizioni di classi parziali per una o più classi di dominio o relazioni di dominio. Scrivere queste definizioni in un nuovo file di codice nel progetto Dsl .
Anteporre a ogni classe questo attributo:
[ValidationState(ValidationState.Enabled)]- Per impostazione predefinita, questo attributo abiliterà anche la convalida per le classi derivate. Se si vuole disabilitare la convalida per una classe derivata specifica, è possibile usare
ValidationState.Disabled.
- Per impostazione predefinita, questo attributo abiliterà anche la convalida per le classi derivate. Se si vuole disabilitare la convalida per una classe derivata specifica, è possibile usare
Aggiungere metodi di convalida alle classi. Ogni metodo di convalida può avere qualsiasi nome, ma avere un parametro di tipo ValidationContext.
Deve essere preceduto da uno o più
ValidationMethodattributi:[ValidationMethod (ValidationCategories.Open | ValidationCategories.Save | ValidationCategories.Menu ) ]ValidationCategories specificano quando il metodo viene eseguito.
Per esempio:
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);
}
}
Si notino i punti seguenti su questo codice:
È possibile aggiungere metodi di convalida alle classi di dominio o alle relazioni di dominio. Il codice per questi tipi è in Dsl\Generated Code\Domain*.cs.
Ogni metodo di convalida viene applicato a ogni istanza della relativa classe e alle relative sottoclassi. Nel caso di una relazione di dominio, ogni istanza è un collegamento tra due elementi del modello.
I metodi di convalida non vengono applicati in alcun ordine specificato e ogni metodo non viene applicato alle istanze della relativa classe in un ordine prevedibile.
In genere non è una buona pratica per un metodo di convalida aggiornare il contenuto dell'archivio, in quanto ciò porterebbe a risultati incoerenti. Al contrario, il metodo deve segnalare qualsiasi errore chiamando
context.LogErroroLogWarningLogInfo.Nella chiamata LogError è possibile fornire un elenco di elementi del modello o collegamenti di relazione che verranno selezionati quando l'utente fa doppio clic sul messaggio di errore.
Per informazioni su come leggere il modello nel codice del programma, vedere Esplorazione e aggiornamento di un modello nel codice programma.
L'esempio si applica al modello di dominio seguente. La relazione ParentsHaveChildren ha ruoli denominati Child e Parent.
Categorie di convalida
Nell'attributo ValidationMethodAttribute specificare quando deve essere eseguito il metodo di convalida.
| Categoria | Execution |
|---|---|
| ValidationCategories | Quando l'utente richiama il comando di menu Convalida. |
| ValidationCategories | Quando il file del modello viene aperto. |
| ValidationCategories | Quando il file viene salvato. In caso di errori di convalida, all'utente verrà assegnata la possibilità di annullare l'operazione di salvataggio. |
| ValidationCategories | Quando il file viene salvato. Se si verificano errori da metodi in questa categoria, l'utente viene avvisato che potrebbe non essere possibile riaprire il file. Usare questa categoria per i metodi di convalida che verificano nomi o ID duplicati o altre condizioni che potrebbero causare errori di caricamento. |
| ValidationCategories | Quando viene chiamato il metodo ValidateCustom. Le convalide in questa categoria possono essere richiamate solo dal codice del programma. Per altre informazioni, vedere Categorie di convalida personalizzate. |
Posizione dei metodi di convalida
È spesso possibile ottenere lo stesso effetto inserendo un metodo di convalida su un tipo diverso. Ad esempio, è possibile aggiungere un metodo alla classe Person invece di alla relazione ParentsHaveChildren e iterare sui collegamenti:
[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)
{ ...
Aggregazione dei vincoli di convalida. Per applicare la convalida in un ordine prevedibile, definire un singolo metodo di convalida in una classe proprietario, ad esempio l'elemento radice del modello. Questa tecnica consente anche di aggregare più segnalazioni di errori in un singolo messaggio.
Gli svantaggi sono che il metodo combinato è meno facile da gestire e che i vincoli devono avere tutti lo stesso ValidationCategories. È pertanto consigliabile mantenere ogni vincolo in un metodo separato, se possibile.
Passaggio di valori nella cache del contesto. Il parametro context ha un dizionario in cui è possibile inserire valori arbitrari. Il dizionario persiste per la durata dell'esecuzione della convalida. Un metodo di convalida specifico potrebbe, ad esempio, mantenere un conteggio degli errori nel contesto e usarlo per evitare di inondare la finestra di errore con messaggi ripetuti. Per esempio:
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( ... ); }
Convalida delle moltiplicazioni
I metodi di convalida per verificare la molteplicità minima vengono generati automaticamente per il tuo DSL. Il codice viene scritto in Dsl\Generated Code\MultiplicityValidation.cs. Questi metodi diventano effettivi quando si abilita la convalida nel nodo Editor\Validation in Esplora DSL.
Se si imposta la molteplicità di un ruolo di una relazione di dominio su 1..* o 1..1, ma l'utente non crea un collegamento di questa relazione, verrà visualizzato un messaggio di errore di convalida.
Ad esempio, se il linguaggio DSL ha classi Person e Town e una relazione PersonLivesInTown con una relazione 1..\* nel ruolo Città, verrà visualizzato un messaggio di errore per ogni persona che non ha città.
Esecuzione della convalida dal codice del programma
È possibile eseguire la convalida accedendo o creando un validationController. Se si desidera che gli errori vengano visualizzati all'utente nella finestra di errore, usare ValidationController associato a DocData del diagramma. Ad esempio, se si sta scrivendo un comando di menu, CurrentDocData.ValidationController è disponibile nella classe del set di comandi:
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;
...
Per altre informazioni, vedere Procedura: Aggiungere un comando al menu di scelta rapida.
È anche possibile creare un controller di convalida separato e gestire gli errori manualmente. Per esempio:
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) { ... }
}
Esecuzione della convalida quando si verifica una modifica
Se si vuole assicurarsi che l'utente venga avvisato immediatamente se il modello non è valido, è possibile definire un evento di archivio che esegue la convalida. Per ulteriori informazioni sugli eventi del negozio, vedere I gestori degli eventi propagano le modifiche al di fuori del modello.
Oltre al codice di convalida, aggiungere un file di codice personalizzato al progetto DslPackage , con contenuto simile all'esempio seguente. Questo codice usa l'oggetto ValidationController allegato al documento. Questo controller visualizza gli errori di convalida nell'elenco degli errori di 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);
}
}
}
I gestori vengono chiamati anche dopo le operazioni Annulla o Ripristina che riguardano i collegamenti o gli elementi.
Categorie di convalida personalizzate
Oltre alle categorie di convalida standard, ad esempio Menu e Apri, è possibile definire le proprie categorie. È possibile richiamare queste categorie dal codice del programma. L'utente non può richiamarli direttamente.
Un uso tipico per le categorie personalizzate consiste nel definire una categoria che verifica se il modello soddisfa le precondizioni di uno strumento specifico.
Per aggiungere un metodo di convalida a una categoria specifica, anteporre un attributo simile al seguente:
[ValidationMethod(CustomCategory = "PreconditionsForGeneratePartsList")]
[ValidationMethod(ValidationCategory.Menu)]
private void TestForCircularLinks(ValidationContext context)
{...}
Annotazioni
È possibile anteporre a un metodo il numero [ValidationMethod()] di attributi desiderato. È possibile aggiungere un metodo alle categorie personalizzate e standard.
Per richiamare una convalida personalizzata:
// Invoke all validation methods in a custom category:
validationController.ValidateCustom
(store, // or a list of model elements
"PreconditionsForGeneratePartsList");
Alternative alla convalida
I vincoli di convalida segnalano errori, ma non modificano il modello. Se invece si vuole impedire che il modello diventi non valido, è possibile usare altre tecniche.
Tuttavia, queste tecniche non sono consigliate. In genere è preferibile consentire all'utente di decidere come correggere un modello non valido.
Modificare la modifica per ripristinare la validità del modello. Ad esempio, se l'utente imposta una proprietà al di sopra del massimo consentito, è possibile reimpostare la proprietà sul valore massimo. A tale scopo, definire una regola. Per altre informazioni, vedere Le regole propagano le modifiche all'interno del modello.
Eseguire il rollback della transazione se viene tentata una modifica non valida. È anche possibile definire una regola a questo scopo, ma in alcuni casi è possibile eseguire l'override di un gestore di proprietà OnValueChanging() o di eseguire l'override di un metodo, OnDeleted(). ad esempio Per eseguire il rollback di una transazione, usare this.Store.TransactionManager.CurrentTransaction.Rollback(). Per altre informazioni, vedere Gestori di modifiche del valore della proprietà di dominio.
Avvertimento
Assicurarsi che l'utente sappia che la modifica è stata modificata o sottoposta a rollback. Ad esempio, usare System.Windows.Forms.MessageBox.Show("message").