Laufzeittextgenerierung mithilfe von T4-Textvorlagen

Sie können Textzeichenfolgen in Ihrer Anwendung zur Laufzeit mithilfe von Visual Studio-Laufzeittextvorlagen generieren. Visual Studio muss nicht auf dem Computer installiert sein, auf dem die Anwendung ausgeführt wird. Laufzeitvorlagen werden manchmal als „vorverarbeitete Textvorlagen“ bezeichnet, da die Vorlage zur Kompilierungszeit Code generiert, der zur Laufzeit ausgeführt wird.

Jede Vorlage ist eine Mischung aus dem Text, wie er in der generierten Zeichenfolge erscheint, und Fragmenten von Programmcode. Die Programmfragmente liefern Werte für die Variablen in der Zeichenfolge und steuern auch bedingte und wiederholte Teile.

Die folgende Vorlage kann beispielsweise in einer Anwendung verwendet werden, die einen HTML-Bericht erstellt.

<#@ 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>

Beachten Sie, dass es sich bei der Vorlage um eine HTML-Seite handelt, auf der die Variablenteile durch Programmcode ersetzt wurden. Sie können mit dem Entwurf einer solchen Seite beginnen, indem Sie einen statischen Prototyp der HTML-Seite schreiben. Sie könnten dann die Tabelle und andere Variablenteile durch Programmcode ersetzen, der den Inhalt generiert, der von Mal zu Mal variiert.

Wenn Sie eine Vorlage in Ihrer Anwendung verwenden, ist es einfacher, die endgültige Form der Ausgabe zu erkennen, als wenn Sie beispielsweise eine lange Serie von Schreibanweisungen verwenden. Es ist einfacher und zuverlässiger, Änderungen an der Form der Ausgabe vorzunehmen.

Erstellen einer Laufzeittextvorlage in einer beliebigen Anwendung

So erstellen Sie eine Laufzeittextvorlage

  1. Wählen Sie im Projektmappen-Explorer im Kontextmenü des Projekts Hinzufügen>Neues Element aus.

  2. Wählen Sie im Dialogfeld Neues Element hinzufügen die Option Laufzeit-Textvorlage aus. (In Visual Basic finden Sie diese unter Allgemeine Elemente>Allgemein.)

  3. Geben Sie einen Namen für die Vorlagendatei ein.

    Hinweis

    Der Name der Vorlagendatei wird als Klassenname im generierten Code verwendet. Er sollte daher keine Leerzeichen oder Satzzeichen enthalten.

  4. Wählen Sie Hinzufügen aus.

    Es wird eine neue Datei erstellt, die die Erweiterung .tt hat. Die Eigenschaft Benutzerdefiniertes Tool ist auf TextTemplatingFilePreprocessor festgelegt. Sie enthält die folgenden Zeilen:

    <#@ template language="C#" #>
    <#@ assembly name="System.Core" #>
    <#@ import namespace="System.Linq" #>
    <#@ import namespace="System.Text" #>
    <#@ import namespace="System.Collections.Generic" #>
    
  5. Fügen Sie einen Verweis auf das NuGet-Paket System.CodeDom hinzu.

Konvertieren einer vorhandenen Datei in eine Laufzeitvorlage

Eine gute Möglichkeit zum Erstellen einer Vorlage besteht darin, ein vorhandenes Beispiel für die Ausgabe zu konvertieren. Wenn Ihre Anwendung beispielsweise HTML-Dateien generiert, können Sie zunächst eine einfache HTML-Datei erstellen. Stellen Sie sicher, dass sie ordnungsgemäß funktioniert und dass das Erscheinungsbild stimmt. Fügen Sie sie dann in Ihr Visual Studio-Projekt ein, und konvertieren Sie sie in eine Vorlage.

So konvertieren Sie eine vorhandene Textdatei in eine Laufzeitvorlage

  1. Fügen Sie die Datei in Ihr Visual Studio-Projekt ein. Wählen Sie im Projektmappen-Explorer im Kontextmenü des Projekts Hinzufügen>Vorhandenes Element aus.

  2. Legen Sie die Eigenschaft Benutzerdefinierte Tools der Datei auf TextTemplatingFilePreprocessor fest. Wählen Sie im Projektmappen-Explorer im Kontextmenü der Datei Eigenschaften aus.

    Hinweis

    Wenn die Eigenschaft bereits festgelegt ist, stellen Sie sicher, dass sie TextTemplatingFilePreprocessor und nicht TextTemplatingFileGenerator lautet. Dies kann passieren, wenn Sie eine Datei einschließen, die bereits die Erweiterung .tt hat.

  3. Ändern Sie die Dateinamenerweiterung in .tt. Dieser Schritt ist zwar optional, verhindert aber, dass Sie die Datei in einem falschen Editor öffnen.

  4. Entfernen Sie alle Leerzeichen oder Satzzeichen aus dem Hauptteil des Dateinamens. Beispielsweise wäre „My Web Page.tt“ falsch, aber „MyWebPage.tt“ richtig. Der Dateiname wird als Klassenname im generierten Code verwendet.

  5. Fügen Sie die folgende Zeile am Anfang der Datei ein. Wenn Sie in einem Visual Basic-Projekt arbeiten, ersetzen Sie „C#“ durch „VB“.

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

  6. Fügen Sie einen Verweis auf das NuGet-Paket System.CodeDom hinzu.

Der Inhalt der Laufzeitvorlage

Vorlagenanweisung

Behalten Sie die erste Zeile der Vorlage so bei, wie sie war, als Sie die Datei erstellt haben:

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

Der Sprachparameter hängt von der Sprache Ihres Projekts ab.

Einfacher Inhalt

Bearbeiten Sie die .tt-Datei so, dass sie den Text enthält, den die Anwendung generieren soll. Beispiel:

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

Eingebetteter Programmcode

Sie können Programmcode zwischen <# und #> einfügen. Beispiel:

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

Beachten Sie, dass Anweisungen zwischen <# ... #> und Ausdrücken zwischen <#= ... #> eingefügt werden. Weitere Informationen finden Sie unter Schreiben einer T4-Textvorlage.

Verwenden der Vorlage

Der Code, der aus der Vorlage erstellt wurde

Wenn Sie die .tt-Datei speichern, wird eine untergeordnete .cs- oder .vb-Datei generiert. Um diese Datei im Projektmappen-Explorer anzuzeigen, erweitern Sie den .tt-Dateiknoten. Wählen Sie in einem Visual Basic-Projekt zuerst Alle Dateien anzeigen in der Projektmappen-Explorer-Symbolleiste aus.

Beachten Sie, dass die untergeordnete Datei eine partielle Klasse enthält, die eine Methode mit dem Namen TransformText() enthält. Sie können diese Methode aus Ihrer Anwendung aufrufen.

Generieren von Text zur Laufzeit

In Ihrem Anwendungscode können Sie den Inhalt Ihrer Vorlage mithilfe eines Aufrufs wie diesem generieren:

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

Wenn Sie die generierte Klasse in einem bestimmten Namespace platzieren möchten, legen Sie die Eigenschaft Namespace des benutzerdefinierten Tools in der Textdatei fest.

Debuggen von Laufzeittextvorlagen

Debuggen und testen Sie Laufzeittextvorlagen auf die gleiche Weise wie gewöhnlichen Code.

Sie können einen Breakpoint in einer Textvorlage festlegen. Wenn Sie die Anwendung im Debugmodus von Visual Studio starten, können Sie den Code durchlaufen und Überwachungsausdrücke auf die übliche Weise auswerten.

Übergeben von Parametern im Konstruktor

In der Regel muss eine Vorlage einige Daten aus anderen Teilen der Anwendung importieren. Um dies zu vereinfachen, ist der von der Vorlage erstellte Code eine partielle Klasse. Sie können einen anderen Teil derselben Klasse in einer anderen Datei in Ihrem Projekt erstellen. Diese Datei kann einen Konstruktor mit Parametern, Eigenschaften und Funktionen enthalten, auf die sowohl der in die Vorlage eingebettete Code als auch der Rest der Anwendung zugreifen kann.

Sie können beispielsweise die separate Datei MyWebPageCode.cs erstellen:

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

In der Vorlagendatei MyWebPage.tt können Sie Folgendes schreiben:

<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>

So verwenden Sie diese Vorlage in der Anwendung:

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

Konstruktorparameter in Visual Basic

In Visual Basic enthält die separate Datei MyWebPageCode.vb Folgendes:

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

Die Vorlagendatei kann Folgendes enthalten:

<#@ 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>

Die Vorlage kann aufgerufen werden, indem der Parameter im Konstruktor übergeben wird:

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)

Übergeben von Daten in Vorlageneigenschaften

Eine alternative Möglichkeit zum Übergeben von Daten an die Vorlage besteht darin, der Vorlagenklasse in einer partiellen Klassendefinition öffentliche Eigenschaften hinzuzufügen. Ihre Anwendung kann die Eigenschaften vor dem Aufrufen von TransformText() festlegen.

Sie können Ihrer Vorlagenklasse auch Felder in einer partiellen Definition hinzufügen. Auf diese Weise können Sie Daten zwischen aufeinander folgenden Ausführungen der Vorlage übergeben.

Verwenden von partiellen Klassen für Code

Viele Entwickler*innen bevorzugen es, große Codeabschnitte in Vorlagen zu schreiben. Stattdessen können Sie Methoden in einer partiellen Klasse definieren, die denselben Namen wie die Vorlagendatei hat. Rufen Sie diese Methoden aus der Vorlage auf. Auf diese Weise zeigt die Vorlage deutlicher, wie die Zielausgabezeichenfolge aussieht. Diskussionen über das Erscheinungsbild des Ergebnisses können von der Logik der Erstellung der Daten, die es anzeigt, getrennt werden.

Assemblys und Verweise

Wenn Ihr Vorlagencode auf eine .NET- oder andere Assembly wie System.Xml.dll verweist, fügen Sie ihn auf die übliche Weise zu den Verweisen Ihres Projekts hinzu.

Wenn Sie einen Namespace auf die gleiche Weise wie eine using-Anweisung importieren möchten, können Sie dies mit der import-Anweisung tun:

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

Diese Richtlinien müssen unmittelbar nach der <#@template-Anweisung am Anfang der Datei platziert werden.

Freigegebener Inhalt

Wenn Sie Text haben, der in mehreren Vorlagen vorkommt, können Sie ihn in einer separaten Datei speichern und in jede Datei einfügen, in der er erscheinen soll:

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

Der eingeschlossene Inhalt kann eine beliebige Mischung aus Programmcode und Nur-Text umfassen, und er kann Include-Anweisungen und andere Anweisungen enthalten.

Die Include-Anweisung kann an einer beliebigen Stelle im Text einer Vorlagendatei oder einer eingeschlossenen Datei verwendet werden.

Vererbung zwischen Laufzeittextvorlagen

Sie können Inhalte zwischen Laufzeitvorlagen freigeben, indem Sie eine Basisklassenvorlage schreiben, die abstrahiert werden kann. Verwenden Sie den inherits-Parameter der <@#template#>-Anweisung, um auf eine andere Laufzeitvorlagenklasse zu verweisen.

Vererbungsmuster: Fragmente in Basismethoden

Beachten Sie in dem im folgenden Beispiel verwendeten Muster die folgenden Punkte:

  • Die Basisklasse SharedFragments definiert Methoden innerhalb von Klassenfeatureblöcken <#+ ... #>.

  • Die Basisklasse enthält keinen Freitext. Stattdessen befinden sich alle Textblöcke innerhalb der Klassenfeaturemethoden.

  • Die abgeleitete Klasse ruft die in SharedFragments definierten Methoden auf.

  • Die Anwendung ruft die TextTransform()-Methode der abgeleiteten Klasse auf, transformiert jedoch nicht die Basisklasse SharedFragments.

  • Sowohl die Basis- als auch die abgeleiteten Klassen sind Laufzeittextvorlagen, die Eigenschaft Benutzerdefiniertes Tool wird also auf TextTemplatingFilePreprocessor festgelegt.

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);

Dies ist die resultierende Ausgabe:

begin 1
    Shared Text 2
end 1

Vererbungsmuster: Text im Basistext

Bei diesem alternativen Ansatz zur Vererbung von Vorlagen wird der Großteil des Textes in der Basisvorlage definiert. Die abgeleiteten Vorlagen stellen Daten- und Textfragmente bereit, die in den Basisinhalt passen.

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 #>
<#+
}
#>

Anwendungscode:

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

Ergebnis:

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.

Entwurfszeitvorlagen: Wenn Sie eine Vorlage verwenden möchten, um Code zu generieren, der Teil Ihrer Anwendung wird, lesen Sie Generieren von Code zur Entwurfszeit mithilfe von T4-Textvorlagen.

Laufzeitvorlagen können in jeder Anwendung verwendet werden, bei der die Vorlagen und ihr Inhalt zur Kompilierzeit festgelegt werden. Wenn Sie jedoch eine Visual Studio-Erweiterung schreiben möchten, die Text aus Vorlagen generiert, die zur Laufzeit geändert werden, lesen Sie Aufrufen der Texttransformation in einer VS-Erweiterung.