Generieren von Code zur Entwurfszeit mithilfe von T4-Textvorlagen

Mit T4-Entwurfszeit-Textvorlagen können Sie Programmcode und andere Dateien in Visual Studio-Projekten generieren. In der Regel werden die Vorlagen so geschrieben, dass der Code, der gemäß den Daten aus einem Modell generiert wird, variiert wird. Ein Modell ist eine Datei oder Datenbank, die wichtige Informationen zu den Anforderungen der Anwendung enthält.

In einem Modell kann z. B. ein Workflow entweder als Tabelle oder Diagramm definiert sein. Anhand des Modells können Sie die Software generieren, die den Workflow ausführt. Wenn sich die Anforderungen der Benutzer ändern, kann der neue Workflow problemlos mit den Benutzern besprochen werden. Die erneute Generierung des Codes anhand des Workflows ist zuverlässiger als die manuelle Aktualisierung des Codes.

Hinweis

Ein Modell ist eine Datenquelle, die einen bestimmten Teil einer Anwendung beschreibt. Ein Modell kann ein beliebiges Format in einem beliebigen Datei- oder Datenbanktyp aufweisen. Es muss kein bestimmtes Format besitzen, wie z. B. ein UML-Modell oder ein domänenspezifisches Sprachmodell. Typische Modelle werden als Tabellen oder XML-Dateien dargestellt.

Sie sind wahrscheinlich bereits mit der Codegenerierung vertraut. Wenn Sie Ressourcen in einer .resx-Datei in der Visual Studio-Projektmappe definieren, wird automatisch ein Satz von Klassen und Methoden generiert. Durch die Ressourcendatei können die Ressourcen einfacher und zuverlässiger bearbeitet werden als dies beim Bearbeiten der Klassen und Methoden möglich wäre. Mithilfe von Textvorlagen können Sie Code auf die gleiche Weise aus einer selbst entworfenen Quelle generieren.

Eine Textvorlage enthält eine Mischung des Texts, den Sie generieren möchten, sowie Programmcode, der Variablenteile des Texts generiert. Der Programmcode ermöglicht die Wiederholung oder das bedingte Auslassen von Teilen des generierten Texts. Der generierte Text selbst kann Programmcode sein, der einen Teil der Anwendung bildet.

Erstellen einer T4-Entwurfszeit-Textvorlage

  1. Erstellen Sie ein neues Visual Studio-Projekt oder öffnen Sie ein vorhandenes Projekt.

  2. Fügen Sie dem Projekt eine Textvorlagendatei hinzu, und geben Sie diesem einen Namen mit der Erweiterung .tt.

    Hierzu wählen Sie im Projektmappen-Explorer im Kontextmenü des Projekts die Option Hinzufügen>Neues Element aus. Wählen Sie im Dialogfeld Neues Element hinzufügen im mittleren Bereich Textvorlage aus.

    Beachten Sie, dass die Eigenschaft Benutzerdefiniertes Tool der Datei TextTemplatingFileGenerator ist.

  3. Öffnen Sie die Datei. Sie enthält bereits die folgenden Anweisungen:

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

    Wenn Sie die Vorlage einem Visual Basic-Projekt hinzugefügt haben, ist das Sprachattribut „VB“.

  4. Fügen Sie am Ende der Datei Text hinzu. Beispiel:

    Hello, world!
    
  5. Speichern Sie die Datei .

    Möglicherweise wird eine Sicherheitswarnung angezeigt, in der Sie bestätigen sollen, dass die Vorlage ausgeführt werden soll. Klicken Sie auf OK.

  6. Erweitern Sie im Projektmappen-Explorer den Vorlagendateiknoten. Sie werden eine Datei mit der Erweiterung .txt sehen. Die Datei enthält den Text, der von der Vorlage generiert wird.

    Hinweis

    Wenn es sich um ein Visual Basic-Projekt handelt, müssen Sie auf Alle Dateien anzeigen klicken, um die Ausgabedatei anzuzeigen.

Den Code erneut generieren

In den folgenden Fällen wird eine Vorlage ausgeführt, wobei die untergeordnete Datei generiert wird:

  • Sie bearbeiten die Vorlage und verschieben dann den Fokus auf ein anderes Visual Studio-Fenster.

  • Speichern Sie die Vorlage.

  • Klicken Sie im Menü Erstellen auf Alle Vorlagen transformieren. Dadurch werden alle Vorlagen in der Visual Studio-Projektmappe transformiert.

  • Wählen Sie im Projektmappen-Explorer im Kontextmenü einer beliebigen Datei die Option Benutzerdefiniertes Tool ausführen aus. Verwenden Sie diese Methode, um eine ausgewählte Untergruppe von Vorlagen zu transformieren.

Sie können ein Visual Studio-Projekt auch so einrichten, dass die Vorlagen ausgeführt werden, wenn sich die von den Vorlagen gelesenen Datendateien ändern. Weitere Informationen finden Sie unter Automatisches erneutes Generieren des Codes.

Variablentext generieren

Textvorlagen ermöglichen es Ihnen, den Inhalt der generierten Datei mithilfe von Programmcode zu verändern.

  1. Ändern des Inhalts der .tt-Datei:

    <#@ 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. Speichern Sie die .tt-Datei, und überprüfen Sie die generierte TXT-Datei erneut. Sie enthält die Quadratzahlen der Zahlen 0 bis 10.

    Beachten Sie, dass Anweisungen in <#...#> eingeschlossen sind und einzelne Ausdrücke in <#=...#>. Weitere Informationen finden Sie unter Schreiben einer T4-Textvorlage.

    Wenn Sie den generierenden Code in Visual Basic schreiben, sollte die template-Anweisung language="VB" enthalten. "C#" ist die Standardoption.

Debuggen einer T4-Textvorlage für die Entwurfszeit

So debuggen Sie eine Textvorlage

  • Fügen Sie debug="true" in die template-Anweisung ein. Beispiel:

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

  • Legen Sie in der Vorlage Haltepunkte auf dieselbe Weise fest wie für normalen Code.

  • Wählen Sie im Kontextmenü des Textvorlagendatei im Projektmappen-Explorer T4-Vorlage debuggen aus.

    Die Vorlage wird ausgeführt und an den Breakpoints beendet. Sie können Variablen prüfen und den Code ganz normal durchlaufen.

Tipp

Mit debug="true" wird die Zuordnung des generierten Codes zur Textvorlage genauer, indem mehr Anweisungen zur Zeilennummerierung in den generierten Code eingefügt werden. Wenn Sie diese auslassen, wird die Ausführung möglicherweise durch die Haltepunkte im falschen Zustand angehalten.

Sie können jedoch die Klausel in der template-Direktive lassen, auch wenn Sie nicht debuggen. Dies verursacht nur einen sehr geringen Leistungsverlust.

Generieren von Code oder Ressourcen für die Projektmappe

Abhängig vom Modell können verschiedene Programmdateien generiert werden. Ein Modell ist eine Eingabequelle wie eine Datenbank, eine Konfigurationsdatei, ein UML- oder DSL-Modell oder eine andere Quelle. Normalerweise generieren Sie mehrere Programmdateien aus dem gleichen Modell. Sie erstellen zu diesem Zweck eine Vorlagendatei für jede generierte Programmdatei und lassen das gleiche Modell von allen Vorlagen lesen.

So generieren Sie Programmcode oder Ressourcen

  1. Ändern Sie die output-Direktive, um eine Datei des entsprechenden Typs zu generieren, z. B. .cs, .vb, .resx oder .xml.

  2. Fügen Sie Code ein, durch den der benötigte Projektmappencode generiert wird. Fügen Sie z. B. folgenden Code ein, wenn Sie drei Deklarationen für Felder für ganze Zahlen in einer Klasse generieren möchten:

    
    <#@ 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. Speichern Sie die Datei, und überprüfen Sie die generierte Datei, die nun den folgenden Code enthält:

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

Generieren von Code und generierter Text

Wenn Sie Programmcode generieren, ist es wichtig, den Generierungscode, der in der Vorlage ausgeführt wird, nicht mit dem resultierenden generierten Code zu verwechseln, der Teil der Projektmappe wird. Die beiden Sprachen müssen nicht identisch sein.

Das vorherige Beispiel enthält zwei Versionen. In einer Version liegt der generierende Code in C# vor. In der anderen Version liegt der generierende Code in Visual Basic vor. Der in beiden Versionen generierte Text ist jedoch identisch und befindet sich in einer C#-Klasse.

Ebenso können Sie eine Visual C#-Vorlage verwenden, um Code in einer beliebigen Sprache zu generieren. Der generierte Text muss nicht in einer bestimmten Sprache vorliegen, und es muss sich nicht um Programmcode handeln.

Strukturieren von Textvorlagen

Es wird empfohlen, den Vorlagencode in zwei Teile aufzuteilen:

  • Ein Konfigurations- oder Datensammlungsteil, der Werte in Variablen festlegt, jedoch keine Textblöcke enthält. Im vorherigen Beispiel ist dieser Teil die Initialisierung von properties.

    Dies wird manchmal als Modellabschnitt bezeichnet, da ein speicherinternes Modell erstellt und normalerweise eine Modelldatei gelesen wird.

  • Der Textgenerierungsteil (im vorliegenden Beispiel foreach(...){...}), in dem die Werte der Variablen verwendet werden.

    Dies ist keine notwendige Trennung, doch auf diese Weise wird das Lesen der Vorlage wegen der geringeren Komplexität des Teils, der Text enthält, vereinfacht.

Lesen von Dateien oder anderen Quellen

Im Vorlagencode können Assemblys wie System.XML verwendet werden, um auf eine Modelldatei oder eine Datenbank zuzugreifen. Um Zugriff auf diese Assemblys zu erhalten, müssen Sie wie hier dargestellt Anweisungen einfügen:

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

Die assembly-Direktive macht die angegebene Assembly auf die gleiche Weise für den Vorlagencode verfügbar wie der Abschnitt "Verweise" eines Visual Studio-Projekts. Sie müssen keinen Verweis auf „System.dll“ einschließen, da automatisch darauf verwiesen wird. Die import-Anweisung ermöglicht wie die using-Anweisung in einer normalen Programmdatei die Verwendung von Typen ohne ihre vollqualifizierten Namen.

Nachdem Sie System.IO importiert haben, können Sie z. B. Folgendes schreiben:


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

Öffnen einer Datei mit einem relativen Pfadnamen

Verwenden Sie this.Host.ResolvePath(), um eine Datei aus einem Ort zu laden, der relativ zur Textvorlage ist. Zur Verwendung von this.Host muss hostspecific="true" in template festgelegt werden:

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

Anschließend können Sie z. B. Folgendes schreiben:

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

Sie können auch this.Host.TemplateFile verwenden, was den Namen der aktuellen Vorlagendatei darstellt.

Der Typ von this.Host (in VB Me.Host) ist Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost.

Abrufen von Daten aus Visual Studio

Zur Verwendung der in Visual Studio bereitgestellten Dienste legen Sie das Attribut hostspecific fest und laden die Assembly EnvDTE. Importieren Sie Microsoft.VisualStudio.TextTemplating, welches die Erweiterungsmethode GetCOMService() enthält. Sie können dann IServiceProvider.GetCOMService() verwenden, um auf DTE und andere Dienste zuzugreifen. Beispiel:

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

Tipp

Eine Textvorlage wird in ihrer eigene App-Domäne ausgeführt, und der Zugriff auf Dienste erfolgt durch Marshalling. Unter diesen Umständen ist GetCOMService() zuverlässiger als GetService().

Automatisches erneutes Generieren des Codes

In der Regel werden mehrere Dateien in einer Visual Studio-Projektmappe mit einem Eingabemodell generiert. Jede Datei wird aus einer eigenen Vorlage generiert, die Vorlagen verweisen jedoch alle auf das gleiche Modell.

Wenn sich das Quellmodell ändert, müssen Sie alle Vorlagen in der Projektmappe erneut ausführen. Um dies manuell auszuführen, wählen Sie Alle Vorlagen transformieren im Menü Erstellen aus.

Wenn Sie das Modellierungs-SDK für Visual Studio installiert haben, können alle Vorlagen bei jeder Buildausführung automatisch transformiert werden. Bearbeiten Sie dazu die Projektdatei (.csproj oder .vbproj) in einem Text-Editor und fügen Sie in der Nähe des Endes der Datei nach allen anderen <import>-Anweisungen die folgenden Zeilen hinzu: In einem Projekt im SDK-Stil kann sie überall in der Projektdatei platziert werden.

Hinweis

Das SDK für Textvorlagentransformation und das Modellierungs-SDK für Visual Studio werden automatisch installiert, wenn Sie bestimmte Features von Visual Studio installieren.

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

Weitere Informationen finden Sie unter Codegenerierung in einem Buildprozess.

Fehlerberichterstattung

Zum Anzeigen von Fehler- und Warnmeldungen im Fehlerfenster von Visual Studio stehen Ihnen die folgenden Methoden zur Verfügung:

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

Konvertieren einer vorhandenen Datei in eine Vorlage

Eine hilfreiche Funktion von Vorlagen ist, dass sie den generierten Dateien sehr ähneln und zudem einigen eingefügten Programmcode enthalten. Dadurch ergibt sich eine einfache Methode zum Erstellen einer Vorlage. Erstellen Sie zuerst eine normale Datei als Prototyp, z. B. eine Visual C#-Datei, und fügen Sie dann nach und nach Generierungscode ein, durch den die resultierende Datei verändert wird.

So konvertieren Sie eine vorhandene Datei in eine Entwurfszeitvorlage

  1. Fügen Sie dem Visual Studio-Projekt eine Datei des Typs hinzu, den Sie generieren möchten, z. B. eine .cs-, .vb- oder .resx-Datei.

  2. Testen Sie die neue Datei, um sicherzustellen, dass sie ordnungsgemäß funktioniert.

  3. Ändern Sie in Projektmappen-Explorer die Dateierweiterung in .tt.

  4. Überprüfen Sie die folgenden Eigenschaften der .tt-Datei:

    Eigenschaft Einstellung
    Benutzerdefiniertes Tool = TextTemplatingFileGenerator
    Buildvorgang = None
  5. Fügen Sie am Anfang der Datei die folgenden Zeilen ein:

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

    Wenn Sie den Generierungscode der Vorlage in Visual Basic schreiben möchten, legen Sie das language-Attribut anstelle von "C#" auf "VB" fest.

    Legen Sie das extension-Attribut auf die Dateinamenerweiterung für den zu generierenden Dateityp fest, z. B. .cs, .resx oder .xml.

  6. Speichern Sie die Datei .

    Eine untergeordnete Datei mit der angegebenen Erweiterung wird erstellt. Die Eigenschaften entsprechen dem Dateityp. Die Eigenschaft Buildvorgang einer CS-Datei wäre z. B. Kompilieren.

    Vergewissern Sie sich, dass die generierte Datei den gleichen Inhalt enthält wie die ursprüngliche Datei.

  7. Identifizieren Sie einen Teil der Datei, den Sie ändern möchten. Beispielsweise einen Teil, der nur unter bestimmten Bedingungen angezeigt oder wiederholt wird oder in dem sich bestimmte Werte ändern. Fügen Sie Generierungscode ein. Speichern Sie die Datei, und überprüfen Sie, ob die untergeordnete Datei ordnungsgemäß generiert wurde. Wiederholen Sie diesen Schritt.

Richtlinien für die Codegenerierung

Siehe Richtlinien für das Verfassen von T4-Textvorlagen.