Generazione di testo in fase di esecuzione con modelli di testo T4

È possibile generare stringhe di testo nell'applicazione in fase di esecuzione usando modelli di testo di runtime di Visual Studio. Il computer in cui viene eseguita l'applicazione non deve avere Visual Studio. I modelli di runtime vengono talvolta definiti "modelli di testo pre-elaborati" perché in fase di compilazione il modello genera codice eseguito in fase di esecuzione.

Ogni modello è una combinazione del testo come verrà visualizzato nella stringa generata e frammenti di codice del programma. I frammenti di programma forniscono valori per le parti variabili della stringa e controllano anche parti condizionali e ripetute.

Ad esempio, il modello seguente può essere usato in un'applicazione che crea un report HTML.

<#@ template language="C#" #>
<html><body>
<h1>Sales for Previous Month</h2>
<table>
    <# for (int i = 1; i <= 10; i++)
       { #>
         <tr><td>Test name <#= i #> </td>
             <td>Test value <#= i * i #> </td> </tr>
    <# } #>
 </table>
This report is Company Confidential.
</body></html>

Si noti che il modello è una pagina HTML in cui le parti delle variabili sono state sostituite con il codice del programma. È possibile iniziare la progettazione di una pagina di questo tipo scrivendo un prototipo statico della pagina HTML. È quindi possibile sostituire la tabella e altre parti variabili con il codice del programma che genera il contenuto che varia da un'occasione all'altra.

L'uso di un modello nell'applicazione semplifica la visualizzazione della forma finale dell'output, ad esempio una lunga serie di istruzioni di scrittura. Apportare modifiche alla forma dell'output è più semplice e affidabile.

Creazione di un modello di testo di runtime in qualsiasi applicazione

Per creare un modello di testo di runtime

  1. In Esplora soluzioni scegliere Aggiungi>nuovo elemento dal menu di scelta rapida del progetto.

  2. Nella finestra di dialogo Aggiungi nuovo elemento selezionare Modello di testo di runtime. (In Visual Basic cercare in Elementi>comuni generale.

  3. Digitare un nome per il file modello.

    Nota

    Il nome del file modello verrà usato come nome di classe nel codice generato. Pertanto, non deve avere spazi o punteggiatura.

  4. Scegliere Aggiungi.

    Viene creato un nuovo file con estensione tt. La proprietà Custom Tool è impostata su TextTemplatingFilePreprocessor. Contiene le righe seguenti:

    <#@ template language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.Collections.Generic" #>
    
  5. Aggiungere un riferimento al pacchetto NuGet System.CodeDom.

Conversione di un file esistente in un modello di runtime

Un buon modo per creare un modello consiste nel convertire un esempio esistente dell'output. Ad esempio, se l'applicazione genererà file HTML, è possibile iniziare creando un normale file HTML. Assicurarsi che funzioni correttamente e che l'aspetto sia corretto. Includerlo quindi nel progetto di Visual Studio e convertirlo in un modello.

Per convertire un file di testo esistente in un modello di runtime

  1. Includere il file nel progetto di Visual Studio. In Esplora soluzioni scegliere Aggiungi>elemento esistente dal menu di scelta rapida del progetto.

  2. Impostare la proprietà Custom Tools del file su TextTemplatingFilePreprocessor. In Esplora soluzioni scegliere Proprietà dal menu di scelta rapida del file.

    Nota

    Se la proprietà è già impostata, assicurarsi che sia TextTemplatingFilePreprocessor e non TextTemplatingFileGenerator. Ciò può verificarsi se si include un file che ha già l'estensione .tt.

  3. Modificare l'estensione del nome file in .tt. Anche se questo passaggio è facoltativo, consente di evitare di aprire il file in un editor non corretto.

  4. Rimuovere gli spazi o la punteggiatura dalla parte principale del nome file. Ad esempio, "My Web Page.tt" non è corretto, ma "MyWebPage.tt" è corretto. Il nome file verrà usato come nome di classe nel codice generato.

  5. Inserire la riga seguente all'inizio del file. Se si lavora in un progetto Visual Basic, sostituire "C#" con "VB".

    <#@ template language="C#" #>

  6. Aggiungere un riferimento al pacchetto NuGet System.CodeDom.

Contenuto del modello di runtime

Direttiva modello

Mantenere la prima riga del modello così come è stata creata al momento della creazione del file:

<#@ template language="C#" #>

Il parametro language dipenderà dalla lingua del progetto.

Contenuto normale

Modificare il file con estensione tt in modo che contenga il testo che si desidera generare dall'applicazione. Ad esempio:

<html><body>
<h1>Sales for January</h2>
<!-- table to be inserted here -->
This report is Company Confidential.
</body></html>

Codice programma incorporato

È possibile inserire il codice del programma tra <# e #>. Ad esempio:

<table>
    <# for (int i = 1; i <= 10; i++)
       { #>
         <tr><td>Test name <#= i #> </td>
             <td>Test value <#= i * i #> </td> </tr>
    <# } #>
 </table>

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

Uso del modello

Codice compilato dal modello

Quando si salva il file con estensione tt, viene generato un .cs sussidiario o .vb file. Per visualizzare questo file in Esplora soluzioni, espandere il nodo del file con estensione tt. In un progetto di Visual Basic scegliere Prima di tutto Mostra tutti i file nella barra degli strumenti Esplora soluzioni.

Si noti che il file sussidiario contiene una classe parziale che contiene un metodo denominato TransformText(). È possibile chiamare questo metodo dall'applicazione.

Generazione di testo in fase di esecuzione

Nel codice dell'applicazione è possibile generare il contenuto del modello usando una chiamata simile alla seguente:

MyWebPage page = new MyWebPage();
String pageContent = page.TransformText();
System.IO.File.WriteAllText("outputPage.html", pageContent);

Per inserire la classe generata in uno spazio dei nomi specifico, impostare la proprietà Spazio dei nomi dello strumento personalizzato del file modello di testo.

Debug di modelli di testo di runtime

Eseguire il debug e testare i modelli di testo di runtime nello stesso modo del codice ordinario.

È possibile impostare un punto di interruzione in un modello di testo. Se si avvia l'applicazione in modalità di debug da Visual Studio, è possibile esaminare il codice e valutare le espressioni di controllo nel modo consueto.

Passaggio di parametri nel costruttore

In genere, un modello deve importare alcuni dati da altre parti dell'applicazione. Per semplificare questa operazione, il codice compilato dal modello è una classe parziale. È possibile creare un'altra parte della stessa classe in un altro file del progetto. Tale file può includere un costruttore con parametri, proprietà e funzioni a cui è possibile accedere sia dal codice incorporato nel modello che dal resto dell'applicazione.

Ad esempio, è possibile creare un file separato MyWebPageCode.cs:

partial class MyWebPage
{
    private MyData m_data;
    public MyWebPage(MyData data) { this.m_data = data; }}

Nel file modello MyWebPage.tt è possibile scrivere:

<h2>Sales figures</h2>
<table>
<# foreach (MyDataItem item in m_data.Items)
   // m_data is declared in MyWebPageCode.cs
   { #>
      <tr><td> <#= item.Name #> </td>
          <td> <#= item.Value #> </td></tr>
<# } // end of foreach
#>
</table>

Per usare questo modello nell'applicazione:

MyData data = ...;
MyWebPage page = new MyWebPage(data);
String pageContent = page.TransformText();
System.IO.File.WriteAllText("outputPage.html", pageContent);

Parametri del costruttore in Visual Basic

In Visual Basic il file separato MyWebPageCode.vb contiene:

Namespace My.Templates
  Partial Public Class MyWebPage
    Private m_data As MyData
    Public Sub New(ByVal data As MyData)
      m_data = data
    End Sub
  End Class
End Namespace

Il file modello può contenere:

<#@ template language="VB" #>
<html><body>
<h1>Sales for January</h2>
<table>
<#
    For Each item In m_data.Items
#>
    <tr><td>Test name <#= item.Name #> </td>
      <td>Test value <#= item.Value #> </td></tr>
<#
    Next
#>
</table>
This report is Company Confidential.
</body></html>

Il modello può essere richiamato passando il parametro nel costruttore :

Dim data = New My.Templates.MyData
    ' Add data values here ....
Dim page = New My.Templates.MyWebPage(data)
Dim pageContent = page.TransformText()
System.IO.File.WriteAllText("outputPage.html", pageContent)

Passaggio di dati nelle proprietà del modello

Un modo alternativo per passare i dati al modello consiste nell'aggiungere proprietà pubbliche alla classe modello in una definizione di classe parziale. L'applicazione può impostare le proprietà prima di richiamare TransformText().

È anche possibile aggiungere campi alla classe modello in una definizione parziale. In questo modo è possibile passare dati tra esecuzioni successive del modello.

Usare classi parziali per il codice

Molti sviluppatori preferiscono evitare di scrivere grandi corpi di codice nei modelli. È invece possibile definire metodi in una classe parziale con lo stesso nome del file modello. Chiamare questi metodi dal modello. In questo modo, il modello mostra più chiaramente l'aspetto della stringa di output di destinazione. Le discussioni sull'aspetto del risultato possono essere separate dalla logica di creazione dei dati visualizzati.

Assembly e riferimenti

Se si vuole che il codice del modello faccia riferimento a un file .NET o a un altro assembly, ad esempio System.Xml.dll, aggiungerlo ai riferimenti del progetto nel modo consueto.

Se si vuole importare uno spazio dei nomi nello stesso modo di un'istruzione using , è possibile eseguire questa operazione con la import direttiva :

<#@ import namespace="System.Xml" #>

Queste direttive devono essere inserite all'inizio del file, immediatamente dopo la <#@template direttiva .

Contenuto condiviso

Se si dispone di testo condiviso tra più modelli, è possibile inserirlo in un file separato e includerlo in ogni file in cui dovrebbe essere visualizzato:

<#@include file="CommonHeader.txt" #>

Il contenuto incluso può contenere qualsiasi combinazione di codice programma e testo normale e può contenere altre direttive di inclusione e altre direttive.

La direttiva include può essere usata ovunque all'interno del testo di un file modello o di un file incluso.

Ereditarietà tra modelli di testo di runtime

È possibile condividere il contenuto tra modelli di runtime scrivendo un modello di classe di base, che può essere astratto. Usare il inherits parametro della <@#template#> direttiva per fare riferimento a un'altra classe del modello di runtime.

Modello di ereditarietà: frammenti nei metodi di base

Nel modello usato nell'esempio seguente si notino i punti seguenti:

  • La classe SharedFragments base definisce i metodi all'interno dei blocchi <#+ ... #>di funzionalità della classe .

  • La classe base non contiene testo libero. Al contrario, tutti i relativi blocchi di testo si verificano all'interno dei metodi di funzionalità della classe.

  • La classe derivata richiama i metodi definiti in SharedFragments.

  • L'applicazione chiama il TextTransform() metodo della classe derivata, ma non trasforma la classe SharedFragmentsdi base .

  • Sia le classi di base che le classi derivate sono modelli di testo di runtime; ovvero la proprietà Custom Tool è impostata su TextTemplatingFilePreprocessor.

SharedFragments.tt:

<#@ template language="C#" #>
<#+
protected void SharedText(int n)
{
#>
   Shared Text <#= n #>
<#+
}
// Insert more methods here if required.
#>

MyTextTemplate1.tt:

<#@ template language="C#" inherits="SharedFragments" #>
begin 1
   <# SharedText(2); #>
end 1

MyProgram.cs:

...
MyTextTemplate1 t1  = new MyTextTemplate1();
string result = t1.TransformText();
Console.WriteLine(result);

Output risultante:

begin 1
    Shared Text 2
end 1

Modello di ereditarietà: testo nel corpo di base

In questo approccio alternativo all'uso dell'ereditarietà del modello, la maggior parte del testo viene definita nel modello di base. I modelli derivati forniscono frammenti di dati e testo che rientrano nel contenuto di base.

AbstractBaseTemplate1.tt:

<#@ template language="C#" #>

Here is the description for this derived template:
  <#= this.Description #>

Here is the fragment specific to this derived template:
<#
  this.PushIndent("  ");
  SpecificFragment(42);
  this.PopIndent();
#>
End of common template.
<#+
  // State set by derived class before calling TextTransform:
  protected string Description = "";
  // 'abstract' method to be defined in derived classes:
  protected virtual void SpecificFragment(int n) { }
#>

DerivedTemplate1.tt:

<#@ template language="C#" inherits="AbstractBaseTemplate1" #>
<#
  // Set the base template properties:
  base.Description = "Description for this derived class";

  // Run the base template:
  base.TransformText();

#>
End material for DerivedTemplate1.

<#+
// Provide a fragment specific to this derived template:

protected override void SpecificFragment(int n)
{
#>
   Specific to DerivedTemplate1 : <#= n #>
<#+
}
#>

Codice dell'applicazione:

...
DerivedTemplate1 t1 = new DerivedTemplate1();
string result = t1.TransformText();
Console.WriteLine(result);

Output risultante:

Here is the description for this derived template:
  Description for this derived class

Here is the fragment specific to this derived template:
     Specific to DerivedTemplate1 : 42
End of common template.
End material for DerivedTemplate1.

Modelli in fase di progettazione: se si vuole usare un modello per generare codice che diventa parte dell'applicazione, vedere Generazione di codice in fase di progettazione usando modelli di testo T4.

I modelli di runtime possono essere usati in qualsiasi applicazione in cui i modelli e il relativo contenuto vengono determinati in fase di compilazione. Tuttavia, se si vuole scrivere un'estensione di Visual Studio che genera testo da modelli che cambiano in fase di esecuzione, vedere Richiamo della trasformazione testo in un'estensione di Visual Studio.