T4 Templates und die Code-Generierung in andere Solutions und Verzeichnisse

Standardmässig können Textvorlagen nur eine Ausgabedatei erstellen. Durch die StringBuilder-Variable GenerationEnvironment können aber auch mehrere Dateien erstellt werden, ohne einen grossen Aufwand betreiben zu müssen. Damit die Synchronisierung mit Visual Studio funktioniert ist ein bisschen Basiswissen zum VS Automation Model (EnvDTE) notwendig.

Eine fertige Lösung bringt das Entity Framework in der T4-Include-Datei EF.Utility.CS.ttinclude mit. Diese stellt auch eine Klasse mit den Namen EntityFrameworkTemplateFileManager bereit, mit deren Hilfe mehrere Dateien erstellt werden können. Das schöne an dieser Klasse ist, dass während der Projektsynchronisierung auch Aufräumaktivitäten stattfinden.

Abbildung 1
Abbildung 1 EntityFrameworkTemplateFileManager im Einsatz
Abbildung 2
Abbildung 2 Erstellung einer Datei pro Klasse und Projektsynchronisation mit dem EntityFrameworkTemplateFileManager

Ein Vorteil aus meiner Sicht: Die Übersichtlichkeit ist gewährleistet, da der Textvorlage alle generierten Dateien untergeordnet sind.

Ein Nachteil ist jedoch die fehlende Funktionalität zur Erstellung von Dateien in ein anderes Verzeichnis bzw. einer anderen Projektmappe. In Verbindung mit parametrisierten Textvorlagen wäre dieser Ansatz durchaus praktisch, da so auch redundanter Code eingespart werden könnte. Ein weiterer Nachteil bei grösseren Projekten, die Textvorlagen sind in der ganzen Solution verteilt.

Eine Variante die erzeugten Dateien in einen anderen Speicherort zu generieren ist die Verwendung der Vorlagen aus der T4-Toolbox.

Damit dies auch funktioniert, muss eine Klasse erstellt werden, die von der Template-Klasse abgeleitet wird. Anschliessend muss die TransformText-Methode überschrieben werden. Hier wird nun der Code platziert, der generiert werden soll. Im Template gibt es eine Output-Eigenschaft, über die definiert werden kann, wie die Ausgabedatei heissen und in welches Projekt die Datei erstellt werden soll.

Abbildung 3
Abbildung 3 T4-Toolbox Unterstützung für die Generierung in ein anderes Projekt

An dieser Variante gefällt mir das Setup nicht. Zuerst muss eine Klasse erstellt werden, die Methode TransformText überschrieben und innerhalb dieser Methode die Generierungslogik platziert werden. Ich muss also T4-Infrastrukturcode mit der Generierungslogik mischen. Ein weiterer Nachteil, aus meiner Sicht, ist die relative Pfadangabe zum Zielprojekt. Ich würde in diesem Fall die einfache Angabe von "T4OtherSolution" bevorzugen. Im Zielprojekt ist zudem nicht ersichtlich, welche Dateien von der Textvorlage erstellt worden sind.

Es blieb mir also noch die Möglichkeit einen TemplateFileManager nach meinen Bedürfnissen zu erstellen. Bei der Frage zur Erwartungskonformität gab es die Varianten, konform zur T4Toolbox oder zum EntityFrameworkTemplateFileManager?

Ich habe mich für die Konformität zum EntityFrameworkTemplateFileManager entschieden und das aus folgenden Gründen:

  • Nutze diesen bereits intensiv in meinen Projekten
  • Projektsynchronisation mit Visual Studio
  • Sourcecontrol-Unterstützung
  • Ersetzen des - ohh man der Name ist so laaang - EntityFrameworkTemplateFileManager, wenn notwendig
  • Evtl. EntityFrameworkTemplateFileManager als Basisklasse

Eine weitere Anforderung ist die Möglichkeit zu definieren, ob der TemplateFileManager bestehende Dateien überschreiben darf oder nicht. Es gibt Situationen, da möchte ich die Erstellung der einfachen Dinge durch T4 erledigt haben und Detailanpassungen an der generierten Datei vornehmen können, ohne dass mir die T4-Vorlage diese Änderungen überschreibt. Als Beispiel für so einen Sonderfall kann eine Metaklasse für DataAnnotations herhalten.

Als Erstes probierte ich den OCP-Ansatz und wollte meine Klasse vom EntityFrameworkTemplateFileManager ableiten. Das ging natürlich in die Hose, denn folgende Meldung begrüsste mich:

Der Zugriff auf "Microsoft.VisualStudio.TextTemplating64421ABD7737113CEF418738DFB063BB.GeneratedTextTransformation.EntityFrameworkTemplateFileManager.EntityFrameworkTemplateFileManager(object)" ist aufgrund der Sicherheitsebene nicht möglich.

Der nächste Schritt war dann die Logik in der Include-Datei genauer zu betrachten. Das Ganze ist recht clever gelöst, da es noch eine Klasse VsEntityFramworkTempalteFileManager gibt, die vorwiegend für die Projektsynchronisierung verantwortlich ist. Zudem gibt es noch eine DynamicTextTransformation und einen DynamicHost mit den dazu gehörigen Schnittstellen. Der Vorteil dieses Ansatzes: Der EntityFrameworkTemplateFileManager lässt sich auch in Preprocessed Templates verwenden.

Ein Nachteil: Es lässt sich in meinen Fall nicht so erweitern, dass Regeln wie zum Beispiel das Open Closed Prinzip angewendet werden können. Der Ansatz, die bestehenden Klassen anzupassen, kam für mich nicht infrage, also nutzte ich die klassische Copy & Paste - Methode, um einen Ableger des EntityFrameworkTemplateFileManager zu erstellen, der meine Anforderungen auch erfüllen kann. ;-)

Der DynamicHost und die zugehörige Schnittstelle haben den Suffix 2 bekommen, damit es nicht zu wilden Includes kommt und der TemplateFileManager ohne Konflikte mit der Include-Datei EF.Utility.CS.ttinclude verwendet werden kann. Die beiden Klassen - Sorry die Länge der Bezeichnungen führt bestimmt zu Augenmuskelkater - EntityFrameworkTemplateFileManager und VsEntityFrameworkTemplateFileManager habe ich wieder zusammengeführt, da ich die Unterstützung für Preprocessed Templates nicht brauche. Zudem habe ich ein Teil der Hilfsmethoden aus meiner DteHelper-Klasse in einer Helper-Klasse eingefügt, der Grund dafür: Die wilden Includes, die das Verteilen einzelner Komponenten mit der Zeit zu einer Herausforderung machen würde. Ich möchte das jedoch nicht zur Regel werden lassen, da es auch nicht der optimale Weg ist.

Worin besteht aber nun der Unterschied zum EntityFrameworkTemplateFileManager (EFTFM)?

Zum einen gibt es die Eigenschaft "CanOverrideExistingFile", die eigentlich sprechend ist. Standardmässig wird das Verhalten des EFTFM übernommen, d. h. die Dateien werden bei jeder Änderung neu erzeugt. Wenn die erwähnte Eigenschaft auf "false" gesetzt wird, dann werden bestehende Dateien nicht mehr überschrieben. Einen Anwendungsfall dafür habe ich bereits beschrieben.

Mit der zweiten Eigenschaft "Encoding" kann das Encoding-Format der Ausgabedatei definiert werden, default ist UTF8. Ohne Workaround wird das normalerweise über die Output-Direktive definiert.

Die andere Anpassung ist in der Methode StartNewFile. Im TemplateFileManager ist diese jetzt mit 2 zusätzlichen optionalen Parametern vorhanden. So kann über diese Methode pro Datei ein Projekt und/oder Verzeichnis angegeben werden. Beim Verzeichnis gibt es dabei eine Besonderheit: Es muss das Zielverzeichnis angegeben werden, der Pfad dorthin ist nicht notwendig, da der absolute Pfad durch das Automation Model ermittelt wird. Weitere Anpassungen gibt es nicht.

Nun mal die Funktionsweise an einem kleinen Beispiel:

DemoTFM.tt (T4 Tempalte in C#)
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ include file="TemplateFileManager.CS.ttinclude" #>

<# 
	string[] fileNames = new string[] { "class4.cs", "class5.cs", "class6.cs" };
	var manager = TemplateFileManager.Create(this);
	manager.CanOverrideExistingFile = false;

	manager.StartNewFile("Class1.cs");
	this.WriteLine("// Klasse ist dem Template zugeordnet");
	
	manager.StartNewFile("Class2.cs", folderName:"CodeFolder");
	this.WriteLine("// Klasse wird im Unterordner CodeFolder erstellt");

	manager.StartNewFile("Class3.cs", projectName:"ClassLibrary");
	this.WriteLine("// Klasse wird im Projekt ClassLibrary erstellt");
	foreach (string item in fileNames)
	{
		manager.StartNewFile(item, projectName:"ClassLibrary", folderName:"SubFolder");
		this.WriteLine("// Klassen werden im Projekt ClassLibrary/SubFolder erstellt");		 
	}
	
	manager.Process();
#>

Wenn dieser Code ausgeführt wird, dann werden die Dateien in die jeweiligen Projekte und Verzeichnissen angelegt. Innerhalb des Solution-Explorers sieht es wie in folgender Abbildung aus:

Abbildung 4
Abbildung 4 Erstellte Dateien im Solution-Explorer

Aus der Abbildung 4 wird ersichtlich, dass der TemplateFileManager künstliche Vorlagen mit der Endung txt4 anlegt und darunter die erzeugten Dateien verlinkt. Gerade bei mehreren Generierungsvorlagen besteht in meinen Augen so eine bessere Übersicht, welche Vorlage welchen Quellcode erzeugt hat, aber bekanntlich ist das Geschmackssache. Die Basis-Textvorlage erstellt zudem eine Zusammenfassung in der gleichnamig generierten Datei.

Abbildung 5
Abbildung 5 Log

Ein sehr schöner Vorteil dieser Variante ist auch die Aufräumfunktion. Ändert sich nun ein Verzeichnis bspw. "SubFolder" in "Folder" oder auch das Projekt, müssen die alten Dateien nicht von Hand gelöscht werden, sondern der TemplateFileManager ist in der Lage anhand der generierten Hilfsvorlagen zu ermitteln, welche Vorlagen verwaist sind und entfernt diese samt der verlinkten Dateien aus dem Projekt.

Abbildung 6
Abbildung 6 Generierter Code aus SubFolder wurde entfernt, nachdem in der T4-Vorlage das Zielverzeichnis geändert wurde

Wer mit dem TemplateFileManager auf eigene Gefahr experimentieren möchte, kann diesen über die Code Gallery Menüpunkt T4 Introduction des tangible T4-Editor beziehen oder die Include-Datei hier herunterladen.

Die eine oder andere Anpassung werde ich bei Gelegenheit noch vornehmen.

  •  
  • 0 Kommentar(e)
  •  

Mein Kommentar

Über jeden weiteren Kommentar in diesem Post benachrichtigen.

Zurück

Translate this page

Kategorien

  • [-].NET Development (215)
  • [-]Datenbank (26)
  • HTML (1)
  • Konfiguration (12)
  • Mind Map (10)
  • Off-topic (9)
  • Open Source (3)
  • Qualität (7)
  • Sharepoint (6)
  • Sicherheit (2)

Archiv

Social Bookmarking

Bookmark bei: Mr. Wong Bookmark bei: Webnews Bookmark bei: Icio Bookmark bei: Oneview Bookmark bei: Linkarena Bookmark bei: Favoriten Bookmark bei: Seekxl Bookmark bei: Favit Bookmark bei: Social Bookmarking Tool Bookmark bei: Power Oldie Bookmark bei: Bookmarks.cc Bookmark bei: Newskick Bookmark bei: Newsider Bookmark bei: Linksilo Bookmark bei: Readster Bookmark bei: Folkd Bookmark bei: Yigg Bookmark bei: Digg Bookmark bei: Del.icio.us Bookmark bei: Reddit Bookmark bei: Simpy Bookmark bei: StumbleUpon Bookmark bei: Slashdot Bookmark bei: Netscape Bookmark bei: Furl Bookmark bei: Yahoo Bookmark bei: Spurl Bookmark bei: Google Bookmark bei: Blinklist Bookmark bei: Blogmarks Bookmark bei: Diigo Bookmark bei: Technorati Bookmark bei: Newsvine Bookmark bei: Blinkbits Bookmark bei: Ma.Gnolia Bookmark bei: Smarking Bookmark bei: Netvouz Information