Generazione di codice in fase di progettazione tramite modelli di testo T4

I modelli di testo T4 in fase di progettazione consentono di generare codice programma e altri file nel progetto di Visual Studio. In genere, i modelli vengono scritti in modo da variare il codice generato in base ai dati di un modello. Un modello è un file o un database che contiene informazioni chiave sui requisiti dell'applicazione.

È ad esempio possibile usare un modello per definire un flusso di lavoro come tabella o diagramma. Dal modello è possibile generare il software che esegue il flusso di lavoro. Quando i requisiti degli utenti cambiano, è facile discutere del nuovo flusso di lavoro con gli utenti. La rigenerazione di codice dal flusso di lavoro è più attendibile dell'aggiornamento manuale del codice.

Nota

Un modello è un'origine dati che descrive un particolare aspetto di un'applicazione. Può avere qualsiasi formato, in qualsiasi tipo di file o database. Non è necessario che si tratti di un particolare formato, ad esempio un modello UML o un modello linguistico specifico del dominio. In genere i modelli hanno formato di tabelle o file XML.

Probabilmente si ha già familiarità con la generazione di codice. Quando si definiscono le risorse in un file con estensione resx nella soluzione Visual Studio, viene generato automaticamente un set di classi e metodi. Il file di risorse semplifica e rende più affidabile la modifica delle risorse rispetto alla modifica dei classi e dei metodi. I modelli di testo permettono di generare codice nello stesso modo da un'origine personalizzata.

Un modello di testo include una combinazione del testo da generare e di codice programma che genera le parti variabili del testo. Il codice del programma consente di ripetere o omettere in modo condizionale parti del testo generato. Il testo generato può essere a sua volta codice programma che genererà parte dell'applicazione.

Creare un modello di testo T4 in fase di progettazione

  1. Creare un nuovo progetto di Visual Studio o aprirne uno esistente.

  2. Aggiungere un file di modello di testo al progetto e assegnare un nome con estensione tt.

    A tale scopo, in Esplora soluzioni scegliere Aggiungi>nuovo elemento dal menu di scelta rapida del progetto. Nella finestra di dialogo Aggiungi nuovo elemento selezionare Modello di testo nel riquadro centrale.

    Si noti che la proprietà Custom Tool del file è TextTemplatingFileGenerator.

  3. Apri il file . Includerà già le direttive seguenti:

    <#@ template hostspecific="false" language="C#" #>
    <#@ output extension=".txt" #>
    

    Se il modello è stato aggiunto a un progetto Visual Basic, l'attributo del linguaggio sarà "VB".

  4. Aggiungere testo alla fine del file. Ad esempio:

    Hello, world!
    
  5. Salvare il file.

    Potrebbe essere visualizzata una finestra di messaggio avviso di sicurezza che chiede di confermare che si vuole eseguire il modello. Fare clic su OK.

  6. In Esplora soluzioni espandere il nodo del file modello e si troverà un file con estensione .txt. Il file contiene testo generato dal modello.

    Nota

    Se il progetto è un progetto di Visual Basic, è necessario fare clic su Mostra tutti i file per visualizzare il file di output.

Rigenerare il codice

Nei casi seguenti sarà eseguito un modello, che genera il file secondario:

  • Modificare il modello e quindi modificare lo stato attivo in un'altra finestra di Visual Studio.

  • Scegliere Salva per salvare il modello.

  • Fare clic su Trasforma tutti i modelli nel menu Compila . In questo modo tutti i modelli verranno trasformati nella soluzione Visual Studio.

  • In Esplora soluzioni scegliere Esegui strumento personalizzato dal menu di scelta rapida di qualsiasi file. Usare questo metodo per trasformare un sottoinsieme selezionato di modelli.

È anche possibile configurare un progetto di Visual Studio in modo che i modelli vengano eseguiti quando i file di dati letti sono stati modificati. Per altre informazioni, vedere Rigenerazione automatica del codice.

Genera testo variabile

I modelli di testo permettono di usare il codice programma per variare il contenuto del file generato.

  1. Modificare il contenuto del file .tt:

    <#@ template hostspecific="false" language="C#" #>
    <#@ output extension=".txt" #>
    <#int top = 10;
    
    for (int i = 0; i<=top; i++)
    { #>
       The square of <#= i #> is <#= i*i #>
    <# } #>
    
  2. Salvare il .tt file ed esaminare nuovamente il file .txt generato. Elenca i quadrati dei numeri da 0 a 10.

    Si noti che le istruzioni sono racchiuse tra <#...#> e le singole espressioni tra <#=...#>. Per altre informazioni, vedere Scrittura di un modello di testo T4.

    Se si scrive il codice di generazione in Visual Basic, la template direttiva deve contenere language="VB". "C#" è l'impostazione predefinita.

Debug di un modello di testo T4 in fase di progettazione

Per eseguire il debug di un modello di testo:

  • Inserire debug="true" nella direttiva template. Ad esempio:

    <#@ template debug="true" hostspecific="false" language="C#" #>

  • Impostare punti di interruzione nel modello, esattamente come per il codice normale.

  • Scegliere Debug modello T4 dal menu di scelta rapida del file modello di testo in Esplora soluzioni.

    Il modello viene eseguito e si arresta nei punti di interruzione. È possibile esaminare le variabili ed eseguire il codice un'istruzione alla volta usando le procedure normali.

Suggerimento

debug="true" rende più accurato il mapping del codice generato al modello di testo, inserendo più direttive di numerazione delle righe nel codice generato. Se non si include la clausola, è possibile che i punti di interruzione arrestino l'esecuzione nello stato errato.

Tuttavia, è possibile lasciare la clausola nella direttiva del modello anche quando non si esegue il debug. Ciò provoca solo un minimo calo nelle prestazioni.

Generazione di codice o risorse per la soluzione

È possibile generare file di programma diversi in base al modello. Un modello è un input quale un database, un file di configurazione, un modello UML, un modello DSL o un'altra origine. In genere si generano diversi file di programma dallo stesso modello. Per ottenere questo risultato, creare un file di modello per ogni file di programma generato e fare in modo che tutti i modelli leggano lo stesso modello.

Per generare codice programma o risorse

  1. Modificare la direttiva di output in modo da generare un file di tipo appropriato, ad esempio un file con estensione cs, vb, resx oppure xml.

  2. Inserire codice per la generazione del codice soluzione necessario. Ad esempio, per generare tre dichiarazioni di campo con valore Integer in una classe:

    
    <#@ template debug="false" hostspecific="false" language="C#" #>
    <#@ output extension=".cs" #>
    <# var properties = new string [] {"P1", "P2", "P3"}; #>
    // This is generated code:
    class MyGeneratedClass {
    <# // This code runs in the text template:
      foreach (string propertyName in properties)  { #>
      // Generated code:
      private int <#= propertyName #> = 0;
    <# } #>
    }
    
  3. Salvare il file ed esaminare il file generato, che ora contiene il codice seguente:

    class MyGeneratedClass {
      private int P1 = 0;
      private int P2 = 0;
      private int P3 = 0;
    }
    

Codice di generazione e testo generato

Quando si genera codice programma, è più importante evitare confusione nel codice generato che viene eseguito nel modello e il codice generato risultante che diventa parte della soluzione. Le due lingue non devono essere uguali.

Nell'esempio precedente sono usate due versioni, una con codice di generazione in C# e l'altra con codice di generazione in Visual Basic. Ma il testo generato da entrambi è lo stesso e si tratta di una classe C#.

Allo stesso modo, è possibile usare un modello di Visual C# per generare codice in qualsiasi linguaggio. Il testo generato non deve trovarsi in una determinata lingua e non deve essere codice programma.

Strutturazione di modelli di testo

È consigliabile separare il codice del modello in due parti:

  • Una parte di configurazione o raccolta dati, che imposta i valori nelle variabili, ma non contiene blocchi di testo. Nell'esempio precedente questa parte corrisponde all'inizializzazione di properties.

    Questa è spesso definita la sezione "modello", poiché costruisce un modello in archivio e in genere legge un file di modello.

  • La parte di generazione del testo (foreach(...){...} nell'esempio), che usa i valori delle variabili.

    Questa non è una separazione necessaria, ma è uno stile che semplifica la lettura del modello riducendo la complessità della parte che include testo.

Lettura di file o di altre origini

Per accedere a un file di modello o a un database, il codice del modello può usare assembly quali System.XML. Per ottenere l'accesso a questi assembly, è necessario inserire direttive simili alle seguenti:

<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.IO" #>

La assembly direttiva rende l'assembly specificato disponibile per il codice del modello, allo stesso modo della sezione Riferimenti di un progetto di Visual Studio. Non è necessario includere un riferimento a System.dll, a cui viene fatto riferimento automaticamente. La direttiva import consente di usare tipi senza usare i rispettivi nomi completi, in modo simile alla direttiva using in un normale file di programma.

Ad esempio, dopo aver importato System.IO, è possibile scrivere:


<# var properties = File.ReadLines("C:\\propertyList.txt");#>
...
<# foreach (string propertyName in properties) { #>
...

Apertura di un file con un nome di percorso relativo

Per caricare un file da un percorso relativo al modello di testo, è possibile usare this.Host.ResolvePath(). Per usare this.Host, è necessario impostare hostspecific="true" in template:

<#@ template debug="false" hostspecific="true" language="C#" #>

È quindi possibile scrivere, ad esempio:

<# string filename = this.Host.ResolvePath("filename.txt");
  string [] properties = File.ReadLines(filename);
#>
...
<#  foreach (string propertyName in properties { #>
...

Si può anche usare this.Host.TemplateFile, che identifica il nome del file di modello corrente.

Il tipo di this.Host (in VB Me.Host) è Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost.

Recupero di dati da Visual Studio

Per usare i servizi forniti in Visual Studio, impostare l'attributo hostspecific e caricare l'assembly EnvDTE . Importare Microsoft.VisualStudio.TextTemplating, che contiene il GetCOMService() metodo di estensione. Sarà quindi possibile usare IServiceProvider.GetCOMService() per accedere a DTE e ad altri servizi. Ad esempio:

<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#
  IServiceProvider serviceProvider = (IServiceProvider)this.Host;
  EnvDTE.DTE dte = (EnvDTE.DTE) serviceProvider.GetCOMService(typeof(EnvDTE.DTE));
#>

Number of projects in this VS solution:  <#= dte.Solution.Projects.Count #>

Suggerimento

Un modello di testo è eseguito nel rispettivo dominio di app e l'accesso ai servizi è effettuato tramite marshalling. In questa circostanza, GetCOMService() è più affidabile di GetService().

Rigenerazione automatica del codice

In genere, diversi file in una soluzione di Visual Studio vengono generati con un modello di input. Ogni file è generato dal modello corrispondente, ma i modelli fanno tutti riferimento allo stesso modello.

In caso di modifica al modello di origine, è consigliabile eseguire di nuovo tutti i modelli della soluzione. A tale scopo, scegliere Trasforma tutti i modelli dal menu Compila .

Se Visual Studio Modeling SDK è stato installato, è possibile trasformare automaticamente tutti i modelli ogni volta che si esegue una compilazione. A tale scopo, modificare il file di progetto (con estensione csproj o vbproj) in un editor di testo e aggiungere le righe seguenti vicino alla fine del file, dopo qualsiasi altra <import> istruzione. In un progetto in stile SDK può essere usato in qualsiasi punto del file di progetto.

Nota

Text Template Transformation SDK e Visual Studio Modeling SDK vengono installati automaticamente quando si installano funzionalità specifiche di Visual Studio.

<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v17.0\TextTemplating\Microsoft.TextTemplating.targets" />
<PropertyGroup>
   <TransformOnBuild>true</TransformOnBuild>
   <!-- Other properties can be inserted here -->
</PropertyGroup>

Per altre informazioni, vedere Generazione di codice in un processo di compilazione.

Segnalazione errori

Per inserire messaggi di errore e di avviso nella finestra degli errori di Visual Studio, è possibile usare questi metodi:

Error("An error message");
Warning("A warning message");

Conversione di un file esistente in un modello

Una funzionalità utile dei modelli consiste nel fatto che il loro aspetto è molto simile a quello dei file generati, anche se includono codice programma. Ciò suggerisce un metodo utile per la creazione di un modello. Creare prima un file ordinario come prototipo, ad esempio un file Visual C# e quindi introdurre gradualmente codice di generazione che varia il file risultante.

Per convertire un file esistente in un modello in fase di esecuzione

  1. Nel progetto di Visual Studio aggiungere un file del tipo da generare, ad esempio un .csfile , .vbo .resx .

  2. Testare il nuovo file per assicurarsi che funzioni correttamente.

  3. In Esplora soluzioni modificare l'estensione del file in .tt.

  4. Verificare le proprietà seguenti del file con estensione tt :

    Proprietà Impostazione
    Strumento personalizzato = TextTemplatingFileGenerator
    Azione di compilazione = Nessuno
  5. Inserire le righe seguenti all'inizio del file:

    <#@ template debug="false" hostspecific="false" language="C#" #>
    <#@ output extension=".cs" #>
    

    Se si vuole scrivere il codice di generazione del modello in Visual Basic, impostare l'attributo language su "VB" anziché "C#"su .

    Impostare l'attributo extension sull'estensione del nome file per il tipo di file che si vuole generare, ad esempio .cs, .resx o .xml.

  6. Salvare il file.

    Sarà creato un file secondario, con l'estensione specificata. Le relative proprietà sono corrette per il tipo di file. Ad esempio, la proprietà Build Action di un file .cs sarà Compile.

    Verificare che il file generato includa lo stesso contenuto del file originale.

  7. Identificare una parte del file da variare. Ad esempio, una parte visualizzata solo in determinate condizioni o una parte ripetuta o che include valori variabili specifici. Inserire codice di generazione. Salvare il file e verificare che il file secondario sia stato generato correttamente. Ripetere questo passaggio.

Linee guida per la generazione di codice

Vedere Linee guida per la scrittura di modelli di testo T4.