Entwickler kennen das sicher: Sie haben verschiedene Metadaten in verschiedenen Quellen, die Sie ebenfalls in Ihrem Quellcode brauchen. Ohne passende Hilfsmittel müssen Sie Ihren Quellcode per Hand nachziehen, wenn sich die die Metadaten ändern. Das ist fehleranfällig, zeitaufwendig und frustrierend. Hier können T4-Templates Abhilfe schaffen. Sie bieten eine flexible Möglichkeit an um aus verschiedensten Metadaten Quellen Sourcecode zu generieren. Wie das funktioniert möchte ich in diesem Beitrag kurz vorstellen.
Das Beispiel Szenario für diesen Artikel ist dabei direkt aus einer aktuellen Projektsituation gegriffen. Wobei ich die Umsetzung für diesen Artikel vereinfacht habe, um mich auf das wesentliche konzentrieren zu können.
SQL Struktur
Es geht dabei darum das Nutzerzugriffe protokolliert werden müssen. Bei einem Internetportal ist es beispielsweise interessant und wichtig zu speichern von welcher IP-Adresse sich ein Nutzer einloggt. Dies kann beispielsweise bei einem Missbrauch dabei helfen die Situation zu klären. Hier für wird in unserem Beispiel ein Access-Log im SQL Server angelegt. Die Datenbank sieht hierbei wie folgt aus:
In der Tabelle „LogEntries“ werden dabei die konkreten Logeinträge mit den Verbindungsinformationen des Nutzers gespeichert. Gleichzeitig besitzt jeder Eintrag einen Verweis auf einen Typen. Diese Typen werden in der Tabelle „LogTypes“ angelegt. Ein Typ ist beispielsweise „Login durchgeführt“ oder „Neues Konto angelegt“. Hierüber werden die Logeinträge gruppiert. In meinem Beispiel sind die folgende Einträge in der LogTypes Tabelle zu finden:
C# Logging
Ein neuer Log Eintrag kann nun im C# Code über den Aufruf folgender Funktion angelegt werden:
public void CreateLogEntry(int typeId, string connectionInfoXml);
Auf die konkrete Implementierung dieser Funktion möchte ich nicht eingehen. Prinzipiell erzeugt sie jedoch lediglich einen neuen Eintrag in der LogEntries Tabelle. Ich stelle sie an dieser Stelle nur vor, um das Problem nochmal hervorzuheben. Wie stellt man nun sicher das die typeId mit denen in der SQL Tabelle übereinstimmen? Zieht man diese immer per Hand nach? Was, wenn man dies mal vergisst? Oder eine Änderung falsch übernimmt, weil der Zeitdruck hoch ist?
T4 Templates
An dieser Stelle kommen die T4-Templates zur Hilfe. Diese sind bereits seit einiger Zeit Bestandteil von Visual Studio, jedoch habe ich erst selten erlebt das sie in der Praxis genutzt wurden.
Im Rahmen dieses Beispiels habe ich lediglich eine Visual Studio Konsolen Anwendung angelegt. T4-Templates sind jedoch mit allen Visual Studio Projekten nutzbar.
Hierfür legen wir ein neues T4-Template in Visual Studio über Hinzufügen -> Neues Element ein.
Anschließend wählen wir Textvorlage aus:
Der fertige Code des T4-Templates sieht nun wie folgt aus:
<#@ template debug="false" hostspecific="false" language="C#" #> <#@ assembly name="System.Data" #> <#@ import namespace="System.Data" #> <#@ import namespace="System.Data.SqlClient" #> <#@ output extension=".cs" #> namespace T4.LoggingSample { /// <summary> /// Stellt Konstanten für mögliche Logging Typen bereit /// </summary> class LoggingTypes { <# // Verbindung an SQL Server herstellen SqlConnection connection = new SqlConnection("Server=.;Database=LoggingSample;Trusted_Connection=True;"); connection.Open(); // Alle Logging Typen abfragen SqlCommand cmd = new SqlCommand("SELECT ID, Name FROM LogTypes ORDER BY ID ASC"); cmd.Connection = connection; cmd.CommandType = CommandType.Text; bool isFirst = true; using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { int id = reader.GetInt32(reader.GetOrdinal("ID")); string name = reader.GetString(reader.GetOrdinal("Name")); // Gültigen Variablen Namen erzeugen string constName = name.Replace(' ', '_'); constName = constName.Replace('?', '_'); constName = constName.Replace('#', '_'); constName = constName.Replace("ä", "ae"); constName = constName.Replace("ö", "ae"); constName = constName.Replace("ü", "ae"); constName = constName.Replace("Ä", "ae"); constName = constName.Replace("Ö", "ae"); constName = constName.Replace("Ü", "ae"); // Wert in T4-Template schreiben if (!isFirst) { WriteLine(""); } isFirst = false; WriteLine(" /// <summary>"); WriteLine(" /// " + id.ToString() + ": " + name); WriteLine(" /// </summary>"); WriteLine(" public const int " + constName + " = " + id + ";"); } } connection.Close(); #> } }
Nun zur Erklärung dieses Templates:
Auf den C# Code innerhalb des Templates möchte ich an dieser Stelle nicht näher eingehen. Grundsätzlich fragt er jedoch alle Einträge aus der „LogTypes“ Tabelle ab und schreibt diese anschließend in das Template.
Die generierte CS-Datei sieht nun wie folgt aus:
namespace T4.LoggingSample { /// <summary> /// Stellt Konstanten für mögliche Logging Typen bereit /// </summary> class LoggingTypes { /// <summary> /// 1: Nutzer hat sich eingeloggt /// </summary> public const int Nutzer_hat_sich_eingeloggt = 1; /// <summary> /// 2: Fehlerhafte Login Daten wurden eingegeben /// </summary> public const int Fehlerhafte_Login_Daten_wurden_eingegeben = 2; /// <summary> /// 3: Neues Benutzerkonto wurde erstellt /// </summary> public const int Neues_Benutzerkonto_wurde_erstellt = 3; } }
Wie man sehen kann spiegelt diese Datei nun den aktuellsten Stand aus der oben vorgestellten Tabelle wieder. Die Codegenerierung durch das T4-Template wird immer dann gestartet wenn das Template gespeichert wird oder über Rechtsklick auf die TT-Datei „Benutzerdefiniertes Tool ausführen“ ausgewählt wird:
Fazit
Wie in diesem Beitrag aufgezeigt bieten T4-Templates eine sehr flexible und bequeme Möglichkeit um Code aus verschiedensten Metadaten Quellen zu generieren. Auf diese Weise werden Fehler ausgemerzt, Zeit gespart und die Entwickler entlastet.
Schlagwörter: Codegenerierung, HowTo, Integration, Sonstiges, Templates
Haben Sie Fragen zu diesem Artikel oder brauchen Sie Unterstützung?
Nehmen Sie mit uns Kontakt auf!
Wir unterstützen Sie gerne bei Ihren Vorhaben!