Procedura: definire vincoli di convalida per i modelli UML

In Visual Studio Ultimate è possibile definire vincoli di convalida che testano se il modello soddisfa una condizione specificata. È ad esempio possibile definire un vincolo per assicurarsi che un utente non crei un ciclo di relazioni di ereditarietà. Il vincolo viene richiamato quando l'utente tenta di aprire o salvare il modello e può inoltre essere richiamato manualmente. Se il vincolo ha esito negativo, nella finestra di errore verrà visualizzato un messaggio di errore definito in precedenza. È possibile comprimere questi vincoli in un pacchetto Visual Studio Integration Extension (VSIX) e distribuirlo ad altri utenti di Visual Studio Ultimate (la pagina potrebbe essere in inglese).

È inoltre possibile definire vincoli per la convalida del modello rispetto a risorse esterne quali database.

Nota

Se si desidera convalidare il codice programma in base a un diagramma livello, vedere Aggiunta di strumenti di convalida architettura personalizzati a diagrammi livelli.

Requisiti

Applicazione dei vincoli di convalida

I vincoli di convalida vengono applicati in tre casi: quando si salva un modello; quando si apre un modello e quando si fa clic su Convalida modello UML nel menu Architettura. In ogni caso, verranno applicati unicamente i vincoli definiti per quel caso specifico, anche se in genere ogni vincolo viene definito in modo che sia applicabile in più di un caso.

Gli errori di convalida vengono segnalati nella finestra errori di Visual Studio. Fare doppio clic sull'errore per selezionare gli elementi del modello per cui si sono verificati errori.

Per ulteriori informazioni sull'applicazione della convalida, vedere Procedura: eseguire la convalida di un modello UML.

Definizione di un'estensione della convalida

Per creare un'estensione della convalida per una finestra di progettazione UML, è necessario creare una classe che definisce i vincoli della convalida e incorporarla in un progetto VSIX (Visual Studio Integration Extension). Il progetto VSIX funge da contenitore per l'installazione del vincolo. Esistono due metodi alternativi per definire un'estensione della convalida:

  • Creazione di un'estensione della convalida in un progetto VSIX distinto mediante un modello di progetto. Si tratta del metodo più rapido. Utilizzarlo se non si desidera combinare i vincoli della convalida con altri tipi di estensione quali comandi di menu, elementi della casella degli strumenti personalizzati o gestori movimenti. È possibile definire vari vincoli in una classe.

  • Creazione di progetti di classe di convalida e progetti VSIX distinti. Utilizzare questo metodo se si desidera combinare più tipi di estensione nello stesso progetto VSIX. Ad esempio, se per il comando di menu è previsto che il modello osservi vincoli specifici, è possibile incorporarlo nello stesso progetto VSIX come un metodo di convalida.

Per creare un'estensione della convalida in un progetto VSIX distinto

  1. Nella finestra di dialogo Nuovo progetto, sotto Progetti di modello fare clic su Estensione di convalida.

  2. Aprire il file con estensione .cs nel nuovo progetto e modificare la classe per implementare il vincolo di convalida.

    Per ulteriori informazioni, vedere Implementazione del vincolo di convalida.

    Nota importanteImportante

    Assicurarsi che i file .cs contengano la seguente istruzione using:

    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;

  3. È possibile aggiungere ulteriori vincoli definendo nuovi metodi. Per identificare un metodo come metodo di convalida, è necessario contrassegnarlo con gli attributi allo stesso modo del metodo di convalida iniziale.

  4. Testare i vincoli premendo F5. Per ulteriori informazioni, vedere Esecuzione della convalida.

  5. Installare il comando di menu in un altro computer copiando il file bin\*\*.vsix compilato dal progetto. Per ulteriori informazioni, vedere Installazione dei vincoli di convalida.

Quando si aggiungono altri file .cs, sono in genere richieste le istruzioni using riportate di seguito:

using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Uml.Classes;

Per creare un vincolo di convalida distinto in un progetto Libreria di classi

  1. Creare un progetto Libreria di classi, aggiungendolo a una soluzione VSIX esistente o creando una nuova soluzione.

    1. Scegliere Nuovo dal menu File, quindi fare clic su Progetto.

    2. In Modelli installati fare clic su Visual Basic o Visual C#, quindi nella colonna centrale fare clic su Libreria di classi.

    3. Impostare Soluzione per indicare se si desidera creare una nuova soluzione o aggiungere un componente a una soluzione VSIX già aperta.

    4. Impostare nome e percorso per il progetto, quindi fare clic su OK.

  2. Creare un progetto VSIX, a meno che la soluzione non ne contenga già uno.

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla soluzione, scegliere Aggiungi, quindi Nuovo progetto.

    2. In Modelli installati espandere Visual Basic o Visual C#, quindi fare clic su Extensibility. Nella colonna centrale fare clic su Progetto VSIX.

  3. Impostare il progetto VSIX come progetto di avvio della soluzione.

    • In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto VSIX e scegliere Imposta come progetto di avvio.
  4. In source.extension.vsixmanifest, sotto Contenuto, aggiungere il progetto Libreria di classi come componente MEF.

    1. Aprire source.extension.vsixmanifest

    2. Fare clic su Aggiungi contenuto.

    3. In Seleziona un tipo di contenuto selezionare Componente MEF.

    4. In Selezionare un'origine fare clic su Progetto e selezionare il nome del progetto Libreria di classi.

  5. Fare clic su Seleziona versioni e selezionare le edizioni di Visual Studio in cui si desidera eseguire l'estensione.

  6. Impostare il nome e i campi descrittivi del progetto VSIX. Salvare il file.

Per definire la classe di convalida

  1. Questa routine non è necessaria se si è creata una classe di convalida con un progetto VSIX distinto dal modello di progetto della convalida.

  2. Nel progetto Classe di convalida, aggiungere riferimenti ai seguenti assembly di .NET:

    Microsoft.VisualStudio.Modeling.Sdk.10.0

    Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml

    Microsoft.VisualStudio.Uml.Interfaces

    System.ComponentModel.Composition

  3. Aggiungere un file al progetto Libreria di classi contenente codice analogo all'esempio seguente.

    • Ogni vincolo di convalida è incluso in un metodo contrassegnato da un attributo specifico. Il metodo accetta un parametro di un tipo di elemento del modello. Quando viene richiamata la convalida, tramite il framework di convalida viene applicato ogni metodo di convalida a ogni elemento del modello conforme al tipo di parametro.

    • È possibile inserire questi metodi in qualsiasi classe e spazio dei nomi. Modificarli nel modo desiderato.

    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Linq;
    using Microsoft.VisualStudio.Modeling.Validation;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
    using Microsoft.VisualStudio.Uml.Classes;
    // You might also need the other Microsoft.VisualStudio.Uml namespaces.
    
    namespace Validation
    {
      public class MyValidationExtensions
      {
        // SAMPLE VALIDATION METHOD.
        // All validation methods have the following attributes.
        [Export(typeof(System.Action<ValidationContext, object>))]
        [ValidationMethod(
           ValidationCategories.Save
         | ValidationCategories.Open
         | ValidationCategories.Menu)]
        public void ValidateClassNames
          (ValidationContext context, 
           // This type determines what elements 
           // will be validated by this method:
           IClass elementToValidate)
        {
          // A validation method should not change the model.
    
          List<string> attributeNames = new List<string>();
          foreach (IProperty attribute in elementToValidate.OwnedAttributes)
          {
            string name = attribute.Name;
            if (!string.IsNullOrEmpty(name) && attributeNames.Contains(name))
            {
              context.LogError(
                string.Format("Duplicate attribute name '{0}' in class {1}", name, elementToValidate.Name),
                "001", elementToValidate);
            }
            attributeNames.Add(name);
          }
    
        }
        // Add more validation methods for different element types.
      }
    }
    

Esecuzione di un vincolo di convalida

A scopo di test, eseguire i metodi di convalida in modalità di debug.

Per testare il vincolo di convalida

  1. Premere F5 oppure scegliere Avvia debug dal menu Debug.

    Verrà avviata un'istanza sperimentale di Visual Studio.

    Risoluzione dei problemi. Se non viene avviata una nuova istanza di Visual Studio:

    • Se si dispone di più progetti, verificare che il progetto VSIX sia impostato come progetto di avvio della soluzione.

    • In Esplora soluzioni fare clic con il pulsante destro del mouse sul nome del progetto di avvio o del solo progetto disponibile, quindi scegliere Proprietà. Fare clic sulla scheda Debug nell'editor delle proprietà del progetto. Assicurarsi che la stringa nel campo Avvia programma esterno sia il percorso completo di Visual Studio, in genere:

      C:\Programmi\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe

  2. Nell'istanza sperimentale di Visual Studio aprire o creare un progetto di modello e un diagramma di modellazione.

  3. Per configurare un test per il vincolo di esempio fornito nella sezione precedente:

    1. Aprire un diagramma classi.

    2. Creare una classe e aggiungere due attributi con lo stesso nome.

  4. Fare clic con il pulsante destro del mouse in un punto qualsiasi del diagramma, quindi scegliere Convalida.

  5. Qualsiasi errore nel modello verrà segnalato nella finestra degli errori.

  6. Fare doppio clic sul rapporto degli errori. Se gli elementi menzionati nel rapporto sono visibili sullo schermo, verranno evidenziati.

    Risoluzione dei problemi: se il comando Convalida non viene visualizzato nel menu, verificare quanto indicato di seguito:

    • Il progetto di convalida è elencato come componente MEF nell'elenco Contenuto in source.extensions.manifest nel progetto VSIX.

    • Gli attributi Export e ValidationMethod corretti sono collegati ai metodi di convalida.

    • ValidationCategories.Menu è incluso nell'argomento per l'attributo ValidationMethod ed è composto con gli altri valori utilizzando l'operatore OR logico (|).

    • I parametri di tutti gli attributi Import ed Export sono validi.

Valutazione del vincolo

Il metodo di convalida deve determinare se il vincolo di convalida che si desidera applicare è true o false. Se true, non viene eseguita alcuna operazione. Se false, viene segnalato un errore utilizzando i metodi forniti dal parametro ValidationContext.

Nota

I metodi di convalida non devono modificare il modello. Non è possibile stabilire con certezza il momento e l'ordine con cui i vincoli vengono eseguiti. Se è necessario passare informazioni tra esecuzioni successive di un metodo di convalida durante l'esecuzione di una convalida, è possibile utilizzare la cache del contesto descritta in Coordinamento di più convalide.

Ad esempio, se si desidera assicurarsi che ogni tipo (classe, interfaccia o enumeratore) disponga di un nome che è almeno tre caratteri di lunghezza, è possibile utilizzare questo metodo:

public void ValidateTypeName(ValidationContext context, IType type)
{
  if (!string.IsNullOrEmpty(type.Name) && type.Name.Length < 3)
  {
    context.LogError(
      string.Format("Type name {0} is too short", type.Name),
               "001", type);
   }
 }

Per informazioni sui metodi e i tipi che è possibile utilizzare per spostarsi nel modello e leggerlo, vedere Programmazione con l'API UML.

Informazioni sui metodi dei vincoli di convalida

Ogni vincolo di convalida è definito da un metodo nel formato seguente:

[Export(typeof(System.Action<ValidationContext, object>))]
 [ValidationMethod(ValidationCategories.Save 
  | ValidationCategories.Menu 
  | ValidationCategories.Open)]
public void ValidateSomething
  (ValidationContext context, IClassifier elementToValidate)
{...}

Gli attributi e i parametri di ogni metodo di convalida sono come segue:

[Export(typeof(System.Action <ValidationContext, object>))]

Definisce il metodo come vincolo di convalida utilizzando Managed Extensibility Framework (MEF).

[ValidationMethod (ValidationCategories.Menu)]

Specifica quando eseguire la convalida. Utilizzare l'operatore OR bit per bit (|) se si desidera combinare più opzioni.

Menu = richiamato dal menu Convalida.

Save = richiamato al salvataggio del modello.

Open = richiamato all'apertura del modello. Load = richiamato al salvataggio del modello, ma in caso di violazione avverte l'utente che potrebbe non essere possibile riaprire il modello. Chiamato anche in fase di caricamento, prima che venga analizzato il modello.

public void ValidateSomething

(ValidationContext context,

IElement element)

Sostituisce il secondo parametro IElement con il tipo di elemento a cui si desidera applicare il vincolo. Il metodo del vincolo verrà richiamato per tutti gli elementi nel tipo specificato.

Il nome del metodo non è importante.

È possibile definire il numero di metodi di convalida desiderato, con tipi diversi nel secondo parametro. Quando la convalida viene richiamata, ogni metodo di convalida verrà chiamato su ogni elemento del modello conforme al tipo di parametro.

Segnalazione di errori di convalida

Per creare una segnalazione errori, utilizzare i metodi forniti da ValidationContext:

context.LogError("error string", errorCode, elementsWithError);

  • "error string" viene visualizzato nell'elenco errori di Visual Studio

  • errorCode è una stringa che deve essere un identificatore univoco dell'errore

  • elementsWithError identifica gli elementi nel modello. Quando l'utente fa doppio clic sulla segnalazione errori, viene selezionata la forma che rappresenta questo elemento.

LogError(), LogWarning() e LogMessage() posizionano i messaggi in diverse sezioni dell'elenco errori.

Applicazione dei metodi di convalida

La convalida viene applicata a ogni elemento nel modello, incluse le relazioni e le parti di elementi di maggiori dimensioni, ad esempio gli attributi di una classe e i parametri di un'operazione.

Ogni metodo di convalida viene applicato a ogni elemento conforme al tipo nel secondo parametro. Ciò significa che se si definisce un metodo di convalida con un secondo parametro di IUseCase e un altro con il relativo tipo super IElement, entrambi questi metodi verranno applicati a ogni caso di utilizzo nel modello.

La gerarchia dei tipi viene riepilogata in Tipi di elemento del modello.

È inoltre possibile accedere agli elementi seguendo le relazioni. Ad esempio, se si volesse definire un metodo di convalida in IClass sarebbe possibile scorrere in ciclo le relative proprietà di proprietà:

public void ValidateTypeName(ValidationContext context, IClass c)
{
   foreach (IProperty property in c.OwnedAttributes)
   {
       if (property.Name.Length < 3)
       {
            context.LogError(
                 string.Format(
                        "Property name {0} is too short", 
                        property.Name), 
                 "001", property);
        }
   }
}

Creazione di un metodo di convalida nel modello

Per assicurarsi che un metodo di convalida venga chiamato esattamente una volta durante l'esecuzione di ogni convalida, è possibile convalidare IModel:

using Microsoft.VisualStudio.Uml.AuxiliaryConstructs; ...
[Export(typeof(System.Action<ValidationContext, object>))]
[ValidationMethod(ValidationCategories.Menu)]
public void ValidateModel(ValidationContext context, IModel model)
{  foreach (IElement element in model.OwnedElements)
   { ...

Convalida di forme e diagrammi

I metodi di convalida non vengono richiamati sugli elementi in visualizzazione quali diagrammi e le forme perché lo scopo primario dei metodi di convalida consiste nel convalidare il modello. È tuttavia possibile accedere al diagramma corrente utilizzando il contesto del diagramma.

Nella classe di convalida, dichiarare DiagramContext come proprietà importata:

using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation; 
...
[Import]
public IDiagramContext DiagramContext { get; set; }

In un metodo di convalida, è possibile utilizzare DiagramContext per accedere al diagramma avente lo stato attivo corrente, se presente:

[Export(typeof(System.Action<ValidationContext, object>))]
[ValidationMethod(ValidationCategories.Menu)]
public void ValidateModel(ValidationContext context, IModel model)
{
  IDiagram focusDiagram = DiagramContext.CurrentDiagram;
  if (focusDiagram != null)
  {
    foreach (IShape<IUseCase> useCaseShape in
              focusDiagram.GetChildShapes<IUseCase>())
    { ...

Per registrare un errore, è necessario ottenere l'elemento del modello che la forma rappresenta perché non è possibile passare una forma a LogError:

       IUseCase useCase = useCaseShape.Element;
       context.LogError(... , usecase);

Coordinamento di più convalide

Quando viene richiamata la convalida, ad esempio dall'utente da un menu del diagramma, ogni metodo di convalida viene applicato a ogni elemento del modello. Ciò significa che in una singola chiamata del framework di convalida, è possibile che lo stesso metodo venga applicato molte volte a diversi elementi.

Tale condizione costituisce un problema per le convalide che gestiscono le relazioni tra gli elementi. È possibile ad esempio scrivere una convalida che inizi da un caso di utilizzo e scorra le relazioni include per verificare che non sono presenti cicli. Ma quando il metodo viene applicato a ogni caso di utilizzo di un modello che contiene molti collegamenti include, è probabile che elabori ripetutamente le stesse aree del modello.

Per evitare questa situazione, è disponibile una cache del contesto in cui vengono conservate le informazioni durante l'esecuzione di una convalida. È possibile utilizzarla per passare le informazioni tra diverse esecuzioni dei metodi di convalida. È possibile ad esempio archiviare un elenco di elementi che sono già stati gestiti nell'esecuzione di questa convalida. La cache viene creata all'inizio dell'esecuzione di ogni convalida e non può essere utilizzata per passare le informazioni tra le diverse esecuzioni della convalida.

context.SetCacheValue<T> (name, value)

Archivia un valore.

context.TryGetCacheValue<T> (name, out value)

Ottiene un valore. Restituisce true se ha esito positivo.

context.GetValue<T>(name)

Ottiene un valore.

Context.GetValue<T>()

Ottiene un valore del tipo specificato.

Installazione e disinstallazione di un'estensione

È possibile installare un'estensione di Visual Studio sia nel computer in uso che in altri computer.

Per installare un'estensione

  1. Nel computer trovare il file .vsix compilato dal progetto VSIX.

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto VSIX, quindi scegliere Apri cartella in Esplora risorse.

    2. Individuare il file bin\*\Progetto.vsix

  2. Copiare il file .vsix nel computer di destinazione in cui si desidera installare l'estensione. Si può trattare del computer in uso o di un altro computer.

    • Nel computer di destinazione deve essere presente una delle versioni di Visual Studio specificata in source.extension.vsixmanifest.
  3. Nel computer di destinazione fare doppio clic sul file .vsix.

    Verrà visualizzato Visual Studio Extension Installer e verrà installata l'estensione.

  4. Avviare o riavviare Visual Studio.

Per disinstallare un'estensione

  1. Scegliere Gestione estensioni dal menu Strumenti.

  2. Espandere Estensioni installate.

  3. Selezionare l'estensione, quindi fare clic su Disinstalla.

Raramente, può verificarsi che un'estensione errata non venga caricata creando un rapporto nella finestra di errore. Tale estensione non viene però visualizzata in Gestione estensioni. In tal caso, è possibile rimuovere l'estensione eliminando il file dal percorso seguente, dove % LocalAppData% è in genere NomeUnità:\Users\NomeUtente\AppData\Local:

%LocalAppData%\Microsoft\VisualStudio\10.0\Extensions

Esempio

In questo esempio vengono trovati cicli nella relazione di dipendenza tra gli elementi.

La convalida verrà eseguita sia sul comando di menu di salvataggio che su quello di convalida.

/// <summary>
/// Verify that there are no loops in the dependency relationsips.
/// In our project, no element should be a dependent of itself.
/// </summary>
/// <param name="context">Validation context for logs.</param>
/// <param name="element">Element to start validation from.</param>
[Export(typeof(System.Action<ValidationContext, object>))]
[ValidationMethod(ValidationCategories.Menu 
     | ValidationCategories.Save | ValidationCategories.Open)]
public void NoDependencyLoops(ValidationContext context, INamedElement element)
{
    // The validation framework will call this method
    // for every element in the model. But when we follow
    // the dependencies from one element, we will validate others.
    // So we keep a list of the elements that we don't need to validate again. 
    // The list is kept in the context cache so that it is passed
    // from one execution of this method to another.
    List<INamedElement> alreadySeen = null;
    if (!context.TryGetCacheValue("No dependency loops", out alreadySeen))
    {
       alreadySeen = new List<INamedElement>();
       context.SetCacheValue("No dependency loops", alreadySeen);
    }

    NoDependencyLoops(context, element, 
                new INamedElement[0], alreadySeen);    
}

/// <summary>
/// Log an error if there is any loop in the dependency relationship.
/// </summary>
/// <param name="context">Validation context for logs.</param>
/// <param name="element">The element to be validated.</param>
/// <param name="dependants">Elements we've followed in this recursion.</param>
/// <param name="alreadySeen">Elements that have already been validated.</param>
/// <returns>true if no error was detected</returns>
private bool NoDependencyLoops(ValidationContext context, 
    INamedElement element, INamedElement[] dependants, 
    List<INamedElement> alreadySeen)
{
    if (dependants.Contains(element))
    {
        context.LogError(string.Format("{0} should not depend on itself", element.Name), 
        "Fabrikam.UML.NoGenLoops", // unique code for this error
        dependants.SkipWhile(e => e != element).ToArray()); 
            // highlight elements that are in the loop
        return false;
    }
    INamedElement[] dependantsPlusElement = 
        new INamedElement[dependants.Length + 1];
    dependants.CopyTo(dependantsPlusElement, 0);
    dependantsPlusElement[dependantsPlusElement.Length - 1] = element;

    if (alreadySeen.Contains(element))
    {
        // We have already validated this when we started 
        // from another element during this validation run.
        return true;
    }
    alreadySeen.Add(element);

    foreach (INamedElement supplier in element.GetDependencySuppliers())
    {
        if (!NoDependencyLoops(context, supplier,
             dependantsPlusElement, alreadySeen))
        return false;
    }
    return true;
}

Vedere anche

Concetti

Programmazione con l'API UML

Altre risorse

Procedura: definire e installare un'estensione di modellazione