Condividi tramite


Procedura: generare file da un modello UML

Da un modello UML è possibile generare codice di programma, schemi, documenti, risorse e altri elementi di qualsiasi tipo. Un metodo pratico per generare file di testo da un modello UML consiste nell'utilizzo dei modelli di testo, che consentono di incorporare codice di programma all'interno del testo da generare.

Gli scenari principali sono tre:

  • Generazione di file da un comando di menu o un movimento. Viene definito un comando Visual Studio disponibile nei modelli UML.

  • Generazione di file da un'applicazione. Viene scritta un'applicazione in grado di leggere i modelli UML e generare file.

  • Generazione in fase di progettazione. Viene utilizzato un modello per definire parte delle funzionalità dell'applicazione e il codice, le risorse e altri elementi vengono generati all'interno della soluzione Visual Studio.

Al termine di questo argomento vengono fornite informazioni relative all'utilizzo della funzionalità di generazione di testo. Per ulteriori informazioni, vedere Generazione di codice e modelli di testo (T4).

Generazione di file da un comando di menu

È possibile utilizzare modelli di testo pre-elaborati all'interno di un comando di menu UML. All'interno del codice del modello di testo o in una classe parziale separata, è possibile leggere il modello visualizzato dal diagramma.

Per ulteriori informazioni su queste funzionalità, vedere i seguenti argomenti:

Nell'esempio seguente viene illustrato un approccio adatto per la generazione di testo da un singolo modello, quando si inizia l'operazione da uno dei diagrammi di modello. Per elaborare un modello in un contesto separato, utilizzare ModelBus di Visual Studio per accedere al modello e ai relativi elementi.

Esempio

Per eseguire questo esempio, creare un progetto di estensione di Visual Studio (VSIX). Il nome del progetto di questo esempio è VdmGenerator. Nel file source.extension.vsixmanifest fare clic su Aggiungi contenuto e impostare il campo del tipo su Componente MEF e il percorso di origine facendo riferimento al progetto corrente. Per ulteriori informazioni sull'impostazione di questo tipo di progetto, vedere Procedura: definire un comando di menu in un diagramma di modellazione.

Aggiungere al progetto un file C# contenente il codice riportato di seguito. Questa classe definisce un comando di menu che verrà visualizzato in un diagramma classi UML.

using System;
using System.ComponentModel.Composition;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;

namespace VdmGenerator
{
  [Export(typeof(ICommandExtension))]
  [ClassDesignerExtension]
  public class GenerateVdmFromClasses : ICommandExtension
  {
    [Import] public IDiagramContext DiagramContext { get; set; }
    public void Execute(IMenuCommand command)
    {
      // Initialize the template with the Model Store.
      VdmGen generator = new VdmGen(
             DiagramContext.CurrentDiagram.ModelStore);
      // Generate the text and write it.
      System.IO.File.WriteAllText
        (System.IO.Path.Combine(
            Environment.GetFolderPath(
                Environment.SpecialFolder.Desktop),
            "Generated.txt") 
         , generator.TransformText());
    }
    public void QueryStatus(IMenuCommand command)
    {
      command.Enabled = command.Visible = true;
    }
    public string Text
    { get { return "Generate VDM"; } }
  }
}

Il file riportato di seguito è il modello di testo. Tramite questo file viene generata una riga di testo per ogni classe UML nel modello e una riga per ogni attributo di ciascuna classe. Il codice per la lettura del modello è incorporato nel testo, delimitato da <# ... #>.

Per creare questo file, in Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto, scegliere Aggiungi, quindi fare clic su Nuovo elemento. Selezionare Modello di testo pre-elaborato. Il nome di questo file di esempio deve essere VdmGen.tt. La proprietà Strumento personalizzato del file deve essere TextTemplatingFilePreprocessor. Per ulteriori informazioni sui modelli di testo pre-elaborati, vedere Generazione di file di testo in fase di runtime utilizzando modelli di testo pre-elaborati.

<#@ import namespace="Microsoft.VisualStudio.Uml.Classes" #>
<# 
   foreach (IClass classElement in store.AllInstances<IClass>())
   {
#>
Type <#= classElement.Name #> ::
<#
     foreach (IProperty attribute in classElement.OwnedAttributes)
     {
#>
       <#= attribute.Name #> : <#= 
           attribute.Type == null ? ""
                                  : attribute.Type.Name #> 
<#
     }
   }
#>

Tramite il modello di testo viene generata una classe parziale C# che diventa parte del progetto Visual Studio. In un file separato aggiungere un'altra dichiarazione parziale della stessa classe. Con questo codice viene fornito al modello l'accesso all'archivio modelli UML:

using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
namespace VdmGenerator
{
    public partial class VdmGen
    {
        private IModelStore store;
        public VdmGen(IModelStore s)
        { store = s; }
    }
}

Per testare il progetto, premere F5. Verrà avviata una nuova istanza di Visual Studio. In questa istanza aprire o creare un modello UML che contiene un diagramma classi. Aggiungere alcune classi al diagramma e alcuni attributi a ciascuna classe. Fare clic con il pulsante destro del mouse sul diagramma, quindi fare clic sul comando dell'esempio Generate VDM. Mediante il comando verrà creato il file C:\Generated.txt. Esaminare il file. Il contenuto sarà simile al testo seguente ma con le proprie classi e i propri attributi:

Type Class1 ::
          Attribute1 : int 
          Attribute2 : string 
Type Class2 :: 
          Attribute3 : string 

Generazione di file da un'applicazione

È possibile generare file da un'applicazione in grado di leggere un modello UML. A tale scopo, il metodo più flessibile e affidabile per accedere al modello e ai relativi elementi è ModelBus di Visual Studio.

È inoltre possibile utilizzare l'API di base per caricare il modello e passarlo a modelli di testo tramite le stesse tecniche descritte nella sezione precedente. Per ulteriori informazioni sul caricamento di un modello, vedere Procedura: leggere un modello UML nel codice del programma.

Generazione di file in fase di progettazione

Se il progetto dispone di un metodo standard per l'interpretazione di UML come codice, è possibile creare modelli di testo per generare codice all'interno del progetto da un modello UML. In genere, si disporrà di una soluzione contenente il progetto di modello UML e uno o più progetti per il codice dell'applicazione. Ogni progetto di codice potrà contenere diversi modelli mediante cui generare codice di programma, risorse e file di configurazione, in base al contenuto del modello. Lo sviluppatore può eseguire tutti i modelli facendo clic su Trasforma tutti i modelli sulla barra degli strumenti di Esplora soluzioni. Il codice di programma viene solitamente generato sotto forma di classi parziali, per semplificare l'integrazione delle parti scritte manualmente.

Un progetto Visual Studio di questo tipo può essere distribuito come modello, per consentire a ogni membro di un team di creare progetti che generano codice da un modello allo stesso modo. In genere, il modello fa parte di un pacchetto di estensione che include vincoli di convalida del modello per garantire che vengano soddisfatte le precondizioni del codice di generazione.

Procedura per la generazione di file

  • Per aggiungere un modello a un progetto, selezionare Modello di testo nella finestra di dialogo Aggiungi nuovo file. È possibile aggiungere un modello alla maggior parte dei tipi di progetto, ma non ai progetti di modellazione.

  • La proprietà Strumenti personalizzati del file di modello deve essere TextTemplatingFileGenerator e l'estensione di file deve essere tt.

  • Il modello deve avere almeno una direttiva di output:

    <#@ output extension=".cs" #>

    Impostare il campo dell'estensione in base al linguaggio del progetto.

  • Per consentire l'accesso al modello da parte del codice di generazione, scrivere direttive <#@ assembly #> per gli assembly necessari per leggere un modello UML. Utilizzare ModelingProject.LoadReadOnly() per aprire il modello. Per ulteriori informazioni, vedere Procedura: leggere un modello UML nel codice del programma.

  • Il modello viene eseguito al momento del salvataggio e quando si fa clic su Trasforma tutti i modelli sulla barra degli strumenti di Esplora soluzioni.

  • Per ulteriori informazioni su questo tipo di modelli, vedere Design-Time Code Generation by using T4 Text Templates.

  • In un progetto tipico si disporrà di diversi modelli con cui verranno generati file diversi dallo stesso modello. La prima parte di ogni modello sarà identica. Per ridurre questa duplicazione, spostare le parti comuni in un file di testo separato, quindi richiamarlo tramite la direttiva <#@include file="common.txt"#> in ogni modello.

  • È inoltre possibile definire un processore di direttiva specializzato che consente di fornire parametri al processo di generazione del testo. Per ulteriori informazioni, vedere Customizing T4 Text Transformation.

Esempio

In questo esempio viene generata una classe C# per ogni classe UML nel modello di origine.

Per configurare una soluzione di Visual Studio per questo esempio

  1. Creare un diagramma classi UML in un progetto di modellazione in una nuova soluzione.

    1. Scegliere Nuovo diagramma dal menu Architettura.

    2. Selezionare Diagramma classe UML.

    3. Attenersi ai prompt per creare una nuova soluzione e un nuovo progetto di modellazione.

    4. Aggiungere alcune classi al diagramma trascinando lo strumento Classe UML dalla casella degli strumenti.

    5. Salvare il file.

  2. Creare un progetto C# o Visual Basic nella stessa soluzione.

    • In Esplora soluzioni fare clic con il pulsante destro del mouse sulla soluzione, scegliere Aggiungi, quindi Nuovo progetto. In Modelli installati fare clic su Visual Basic o Visual C#, quindi selezionare un tipo di progetto, ad esempio Applicazione console.
  3. Aggiungere un file di testo normale al progetto C# o Visual Basic. Questo file conterrà codice condiviso nel caso occorra scrivere diversi modelli di testo.

    • In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto, scegliere Aggiungi, quindi fare clic su Nuovo elemento. Selezionare File di testo.

    Inserire il testo visualizzato nella sezione seguente.

  4. Aggiungere un file modello di testo al progetto C# o Visual Basic.

    • In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto, scegliere Aggiungi, quindi fare clic su Nuovo elemento. Selezionare Modello di testo.

    Inserire il codice che segue nel file modello di testo.

  5. Salvare il file modello di testo.

  6. Esaminare il codice nel file sussidiario. Deve contenere una classe per ogni classe UML nel modello.

    1. In un progetto Visual Basic fare clic su Mostra tutti i file sulla barra degli strumenti di Esplora soluzioni.

    2. In Esplora soluzioni espandere il nodo del file modello.

Contenuto del file di testo condiviso

In questo esempio il file è denominato SharedTemplateCode.txt e si trova nella stessa cartella dei modelli di testo.

<# /* Common material for inclusion in my model templates */ #>
<# /* hostspecific allows access to the Visual Studio API */ #>
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="Microsoft.VisualStudio.Uml.Interfaces.dll"#>
<#@ assembly name="Microsoft.VisualStudio.ArchitectureTools.Extensibility.dll"#>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.Uml.Classes" #>
<#@ import namespace="Microsoft.VisualStudio.ArchitectureTools.Extensibility" #>
<#@ import namespace="Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml" #>
<#+  // Note this is a Class Feature Block
///<summary>
/// Text templates are run in a common AppDomain, so 
/// we can cache the model store that we find.
///</summary>
private IModelStore StoreCache
{
  get { return AppDomain.CurrentDomain.GetData("ModelStore") as IModelStore; }
  set { AppDomain.CurrentDomain.SetData("ModelStore", value); } 
}
private bool CacheIsOld()
{
    DateTime? dt = AppDomain.CurrentDomain
           .GetData("latestAccessTime") as DateTime?;
    DateTime t = dt.HasValue ? dt.Value : new DateTime(); 
    DateTime now = DateTime.Now;
    AppDomain.CurrentDomain.SetData("latestAccessTime", now);
    return now.Subtract(t).Seconds > 3;
}

///<summary>
/// Find the UML modeling project in this solution,
/// and load the model.
///</summary>
private IModelStore ModelStore
{
  get 
  {
    // Avoid loading the model for every template:
    if (StoreCache == null || CacheIsOld())
    {
      // Use Visual Studio API to find modeling project:
      EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host)
                       .GetService(typeof(EnvDTE.DTE));
      EnvDTE.Project project = null;
      foreach (EnvDTE.Project p in dte.Solution.Projects)
      {
        if (p.FullName.EndsWith(".modelproj"))
        {
          project = p;
          break;
        }            
      }
      if (project == null) return null;

      // Load UML model into this AppDomain
      // and access model store:
      IModelingProjectReader reader = 
           ModelingProject.LoadReadOnly(project.FullName);
      StoreCache = reader.Store;
    }
    return StoreCache;
  }
}
#>

Contenuto del file modello di testo

Il testo seguente viene inserito nel file .tt. In questo esempio vengono generate classi in un file C# dalle classi UML del modello. È tuttavia possibile generare file di qualsiasi tipo. Il linguaggio del file generato non è correlato al linguaggio in cui è stato scritto il codice del modello di testo.

<#@include file="SharedTemplateCode.txt"#>
<#@ output extension=".cs" #>
namespace Test
{
<#
      foreach (IClass c in ModelStore.AllInstances<IClass>())
      {
#>
   public partial class <#=c.Name#>
   {   }
<#
      }
#>
}

Modalità di utilizzo della funzionalità di generazione di testo

Per sfruttare le potenzialità effettive della modellazione, utilizzare i modelli per progettare a livello di requisiti o architettura. Tramite i modelli di testo è possibile convertire in parte le idee di alto livello in codice. In molti casi non è possibile trovare una corrispondenza esatta tra gli elementi dei modelli UML e le classi o altre parti del codice di programma.

La trasformazione dipende inoltre dal dominio del problema, in quanto non esiste un mapping universale tra modelli e codice.

Di seguito vengono presentati alcuni esempi di generazione del codice dai modelli:

  • Linee di prodotti. Fabrikam, Inc. fabbrica e installa sistemi di gestione dei bagagli per gli aeroporti. Gran parte del software è molto simile nelle diverse installazioni, ma la configurazione del software varia in base al macchinario di gestione dei bagagli installato e dalla modalità di interconnessione delle varie parti mediante nastri trasportatori. All'inizio di un contratto gli analisti di Fabrikam discutono i requisiti con la società che gestisce l'aeroporto e delineano il piano dei componenti hardware tramite un diagramma di attività UML. Da questo modello il team di sviluppo genera file di configurazione, codice di programma, piani e documenti utente. Il lavoro viene completato dalle aggiunte e dalle modifiche manuali apportate al codice. Acquisendo esperienza da un progetto all'altro, ampliano l'ambito del materiale generato.

  • Criteri. Gli sviluppatori di Contoso, Ltd creano spesso siti Web e progettano lo schema di navigazione tramite diagrammi classi UML. Ogni pagina Web è rappresentata da una classe e le associazioni rappresentano i collegamenti di navigazione. Gli sviluppatori generano gran parte del codice di un sito Web dal modello. Ogni pagina Web corrisponde a diverse classi e voci di file di risorse. Con questo metodo si ha il vantaggio di poter costruire ogni pagina sulla base di un solo criterio, ottenendo maggiore affidabilità e flessibilità rispetto al codice scritto a mano. Il criterio si applica ai modelli di generazione, mentre il modello viene utilizzato per acquisire gli aspetti variabili.

  • Schemi. Humongous Insurance ha migliaia di sistemi in tutto il mondo. In questi sistemi vengono utilizzati database, linguaggi e interfacce di diversi tipi. Il team di architettura centrale pubblica internamente modelli di concetti e processi aziendali. Da tali modelli, i team locali generano parti degli schemi di database e scambio, dichiarazioni in codice di programma e così via. La presentazione grafica dei modelli agevola la valutazione delle proposte da parte dei team. I team creano più diagrammi che illustrano subset del modello applicabili alle diverse aree aziendali. Utilizzano inoltre i colori per evidenziare le aree soggette a modifiche.

Tecniche importanti per la generazione di elementi

Negli esempi presentati in precedenza i modelli vengono utilizzati per vari scopi specifici delle aziende e l'interpretazione degli elementi di modellazione, quali classi e attività, varia da un'applicazione a un'altra. Le tecniche seguenti sono utili per generare elementi dai modelli.

  • Profili. Anche all'interno di un'area aziendale, l'interpretazione di un tipo di elemento può variare. In un diagramma di sito Web, ad esempio, alcune classi possono rappresentare le pagine Web e altre i blocchi di contenuto. Per semplificare l'individuazione di queste distinzioni da parte degli utenti, è possibile definire stereotipi. Gli stereotipi rendono inoltre possibile il collegamento di proprietà aggiuntive che si applicano a elementi di un determinato tipo. Gli stereotipi sono inclusi nel pacchetto dei profili. Per ulteriori informazioni, vedere Procedura: definire un profilo per estendere UML.

    Nel codice dei modelli è facile accedere agli stereotipi definiti in un oggetto. Ad esempio:

    public bool HasStereotype(IClass c, string profile, string stereo)
    { return c.AppliedStereotypes.Any
       (s => s.Profile == profile && s.Name == stereo ); }
    
  • Modelli vincolati. Non tutti i modelli che è possibile creare sono validi per ogni scopo. Nei modelli dei sistemi di gestione dei bagagli per gli aeroporti della società Fabrikam, ad esempio, sarebbe errato che un banco check-in fosse privo di un nastro trasportatore in uscita. È possibile definire alcune funzioni di convalida che agevolino il rispetto di questi vincoli da parte degli utenti. Per ulteriori informazioni, vedere Procedura: definire vincoli di convalida per i modelli UML.

  • Mantenimento delle modifiche manuali. Solo alcuni file della soluzione possono essere generati da un modello. Nella maggior parte dei casi è necessario aggiungere o modificare manualmente il contenuto generato. È importante tuttavia che queste modifiche manuali vengano mantenute quando viene nuovamente eseguita la trasformazione del modello.

    Nei casi in cui i modelli generino codice nei linguaggi .NET, è necessario che vengano generate classi parziali per consentire agli sviluppatori di aggiungere metodi e codice. È inoltre utile generare ogni classe come una coppia, formata da una classe di base astratta che contiene i metodi e una classe che eredita che contiene solo il costruttore. Ciò consente agli sviluppatori di eseguire l'override dei metodi. Per consentire l'override dell'inizializzazione, l'operazione viene eseguita in un metodo separato, anziché nei costruttori.

    Nei casi in cui un modello generi XML e altri tipi di output, può essere più difficile tenere il contenuto manuale separato dal contenuto generato. A tale scopo, creare un'attività nel processo di compilazione che combina due file. In alternativa, gli sviluppatori possono modificare una copia locale del modello di generazione.

  • Spostamento del codice in assembly separati. Non è consigliabile scrivere grandi quantità di codice nei modelli. È preferibile tenere separato il contenuto generato dal calcolo e i modelli di testo non sono supportati in modo ottimale per la modifica del codice.

    Al contrario, se è necessario eseguire calcoli sostanziali per generare testo, compilare tali funzioni in un assembly separato e chiamare i metodi dal modello.