17. Jan 2022
Lesedauer 16 Min.
Agil Code generieren
Methoden der Softwareentwicklung, Teil 2
Ob CodeDOM, T4 oder Excel: Praxisbeispiele zeigen, wie Sie damit Code erzeugen.

Warum Code selbst eintippen, wenn der Computer Code wesentlich schneller erzeugen kann? Viele Softwareentwickler setzen daher Codegenerierung ein oder haben zumindest schon einmal über den Einsatz nachgedacht. Der erste Teil dieser Artikelserie [1] bot eine Entscheidungshilfe an, indem er Vor- und Nachteile der Codegenerierung und ihren Platz in der modellgetriebenen Softwarearchitektur (MDA) diskutiert hat. Ferner wurden dort einige empfohlene Vorgehensweisen (Best Practices) und Anwendungsfälle erklärt.Kurz zusammengefasst ergibt sich das folgende Bild: Codegenerierung bietet viele Vorteile für Entwickler wie höhere Produktivität und Agilität sowie bessere Qualität, Stabilität und Einheitlichkeit des generierten Codes. Die Codegenerierung beseitigt auch einige Nachteile von anderen Methoden für repetitiven oder generischen Code – anstelle von Copy-and-paste oder generischen Klassen beziehungsweise Bibliotheken. Das mündet in die folgende Erkenntnis: Wo es sinnvoll (siehe Abschnitt „Code Generation: Wann anwenden?“ in [1]) und vom Projektbudget her vertretbar ist, sollte man so oft und so aktiv wie möglich die modellgetriebenen Ansätze mit der Codegenerierung anwenden.Dieser Artikel will anhand von praktischen Beispielen Wege zeigen, wie Sie Quellcode mit .NET und Visual Studio generieren. Der ultimative Fall kommt sogar ganz ohne .NET Framework aus – SQL/C#-Quellcode wird nur mithilfe einer Excel-Datei erzeugt.Alle Demoprojekte finden Sie im GitHub-Repository [2]. Von dort können Sie sie klonen beziehungsweise herunterladen und gegebenenfalls als Vorlagen für eigene Codegenerierungslösungen verwenden. Weitere ausführliche Informationen zur Codegenerierung in .NET und Visual Studio finden Sie in zahlreichen Online- und gedruckten Medien – zum Beispiel ist [3] ein guter Startpunkt dafür.
Prozess der Modellierung und Codegenerierung
Bevor wir uns ab dem nächsten Abschnitt mit konkreten Anwendungsfällen beschäftigen, erläutert der Autor noch das Paradigma (Bild 1) und die daraus resultierenden Modellierungs- und Codegenerierungsprozesse (Bild 2). Das macht die Vorgehensweisen in den entsprechenden Lösungen mit Codegenerierung klarer und verständlicher. Ein Modellierungsprozess sieht in der Regel folgendermaßen aus:
Paradigmavon modellgetriebener Softwareentwicklung(Bild 1)
Autor

Prozessder Codegenerierung(Bild 2)
Autor
- Geschäftsdomäne definieren: Sie analysieren das zu lösende Geschäftsproblem beziehungsweise die Aufgabe und ordnen es beziehungsweise sie der entsprechenden Geschäftsdomäne zu. Für eine Buchhaltungslösung gilt das Rechnungswesen und für ein Computerspiel wäre die Gaming-Unterhaltung als Geschäftsdomäne denkbar.
- Metamodell erstellen/auswählen: Nachdem Sie die Geschäftsdomäne gewählt haben, können Sie ein Metamodell erstellen beziehungsweise ein vorhandenes anpassen, falls kein passendes Metamodell für das zu lösende Problem in der gewählten Geschäftsdomäne vorhanden sein sollte.
- Geschäftsmodell erstellen: Im Rahmen des verwendeten Metamodells und auf der fachlichen Abstraktionsebene der relevanten Domäne werden benötigte Werte, Beziehungen und andere Fakten spezifiziert, die eine Lösung des aktuellen fachlichen Problems formal beschreiben.
- Modell (Geschäftsmodell): Der wichtigste und zentrale Teil, in dem alle Fakten eines Anwendungsfalls auf einer hohen Abstraktionsebene definiert sind: die Geschäftsobjekte und gegebenenfalls deren Beziehungen und Interaktionen.
- Metamodell: Beschreibt den Definitionsraum eines Geschäftsmodells (siehe [4] und den Kasten Metamodell).
- Codegenerator: Ein wichtiger zentraler Baustein, sozusagen ein Motor der modellgetriebenen Softwareentwicklung. Stellt ein Programm oder ein Werkzeug dar, das die Transformationen von einer höher abstrahierten Ebene in Form eines Geschäftsmodells beziehungsweise DSL-Codes in entsprechenden spezifischen Code (mit niedriger Abstraktion) beziehungsweise Modell durchführt.
- Vorlage (englisch: Template): Ein statischer Modell-agnostischer (unabhängiger) Teil des zu generierenden Codes. Es ist ein Skelett des zu generierenden Codes mit Platzhaltern für die Geschäftsmodelldaten.
- Generat: Spezifische wohlformalisierte Artefakte, die das Ziel der Codegenerierung darstellen: Programmcode, spezifische Modelle, Konfigurationen, Skripte et cetera.
- Eingabe einlesen: Zuerst liest man spezifische Daten aus einem Modell (Geschäftsmodell) ein. Ein Modell kann dabei in verschiedenen Modalitäten vorhanden sein: in textuellem und/oder grafischem beziehungsweise tabellarischem Format. Man kann sogar ein Geschäftsmodell innerhalb einer öffentlichen, statischen OO-Klasse definieren.
- Modellgraphen aufbauen: Basierend auf dem zugrunde liegenden Metamodell baut man im Programmspeicher einen Modellgraphen auf und füllt ihn mit den im ersten Prozessschritt eingelesenen Geschäftsmodelldaten. Ein Modellgraph kann ebenso wie ein Geschäftsmodell verschiedene Formen annehmen – zum Beispiel Document Object Model, kurz DOM [5], Abstract Syntax Tree, kurz AST [6], beziehungsweise eine andere dem jeweiligen Metamodell entsprechende Datenstruktur.
- Transformation durchführen: Ein in einen Modellgraphen geladenes Geschäftsmodell wird durch den Codegenerator in das/die gewünschten Ausgabeformate wie zum Beispiel C#, Java, Python, XML, HTML, JSON und so weiter umgewandelt. Es können dabei gegebenenfalls eine oder mehrere Vorlagen (Templates) verwendet werden.
- Ausgabe persistieren: Der im letzten Schritt generierte Code wird in einer Datei gespeichert beziehungsweise in einen anderen Datenstrom (wie zum Beispiel einen Cloud-Speicher) kopiert.
Definition von Meta- und Geschäftsmodellen
Um den Lesern das Verständnis des gesamten Modellierungs- sowie Codegenerierungsprozesses zu erleichtern, werden wir nun eine wohlbekannte Domäne mit einem einfach zu lösenden Problem auswählen und dort ein überschaubares Metamodell aufbauen.In unserem Beispiel wird eine planare (flache) 2D-Geometrie die Rolle der Geschäftsdomäne spielen und als eine Aufgabe (Geschäftsproblem) werden wir die Berechnung der Fläche und des Umfangs einer planaren geometrischen Figur wie Kreis, Rechteck und so weiter festlegen. In diesem Fall kann wohl jeder ein Geschäftsexperte sein!In der Tabelle 1 ist ein konzeptionelles Metamodell dargestellt, das notwendige Konzepte beschreibt, die es erlauben, Geschäftsmodelle zu entwerfen, um die Berechnung von Fläche (Area) und Umfang (Perimeter) sowie einigen anderen Eigenschaften eines Polygons (2D-Figur) formal zu beschreiben. In Tabelle 2, Tabelle 3 und Tabelle 4 (Tabelle 4 finden Sie aus Platzgründen nur in den Downloads zum Artikel) werden wiederum entsprechende Polygonmodelle jeweils für Kreis-, Rechteck- und Trapez-Figuren spezifiziert.Tabelle 2: Polygon-Modell - Kreis
|
Wie man in den Polygonmodellen sieht, sind dort nicht nur abstrakte (mathematische), sondern auch sprachspezifische (C#/.NET) formelle Ausdrücke für relevante Eigenschaften definiert. Ferner sind konkrete jeweilige Dateneingaben gemacht (zum Beispiel Radiuswert für den Kreis oder Breite und Höhe für das Rechteck und so weiter), mit denen die Zieleigenschaften (Fläche/Umfang) und gegebenenfalls die Co-Eigenschaften berechnet werden können. Das wird unmittelbar bei der Umsetzung der Codegenerierung behilflich sein.Nun haben wir die Geometrie als Geschäftsdomäne der zu lösenden Aufgabe der Flächen- und Umfangsberechnung sowie ein dazu passendes Metamodell mit entsprechenden konkreten Polygonmodellen für Kreis, Rechteck und Trapez definiert. Damit gilt der Modellierungsprozess als abgeschlossen und es kann mit der Codegenerierung weitergehen.
Die Demoprojektmappe
Auf der GitHub-Seite unter [2] finden Sie die Projektmappe AgileCodeGeneration mit Demoprojekten, die in den nachfolgenden Abschnitten referenziert werden. Um die Demoprojekte lokal zu beziehen, öffnen Sie den Link [2], klicken dann auf die grüne Schaltfläche Code und klonen das Git-Repository beziehungsweise laden es als ZIP-Datei herunter. Entpacken Sie es und öffnen Sie schließlich in Visual Studio 2019/2022 die Projektmappe AgileCodeGeneration.sln. Dort finden Sie in den folgenden Verzeichnisse die Artefakte vor:- 1. Models | GeometricModels: Eine Bibliothek mit den Kreis- und Trapez-Modellen (als statische öffentliche C#-Klassen).
- 1. Models | GeometricModelsCore: Eine Bibliothek mit dem Rechteck-Modell (als statische öffentliche C#-Klasse).
- 2. Code Generation - Direct | GenExampleDirect: Eine per Hand umgesetzte Codegenerierung.
- 3. Code Generation - CodeDOM | GenExampleCodeDOM: Codegenerierungsprojekt mit CodeDOM-Technologie.
- 3. Code Generation - CodeDOM | TargetCodeDOM: Zielprojekt für CodeDOM-Codegenerierung.
- 4. Code Generation - T4 | 4.1_static | GenExampleT4: Codegenerierungsprojekt mit T4-Textvorlage.
- 4. Code Generation - T4 | 4.2_dynamic | GenExampleT4Dynamic: Codegenerierungsprojekt mit T4-Laufzeit-Textvorlagen.
- 4. Code Generation - T4 | 4.2_dynamic | TargetRuntimeT4: Zielprojekt für Codegenerierung mit T4-Laufzeit-Textvorlagen.
- 5. Code Generation - Excel | ExcelSqlCodeGen.xlsx: Excel-Codegenerierungsmodell für SQL-Skripte.
- 5. Code Generation - Excel | PolygonCodeGenerator.xlsm: Excel-Codegenerierungsmodell für C#-Klassen.
- 5. Code Generation - Excel | PolygonTestCircle/-Rectangle/-Trapezoid: C#-Zielprojekte für die Codegenerierung mit Excel-Modell.
Codegenerierung per Hand
Die .NET-Plattform und Visual Studio besitzen einige Codegenerierungstechnologien, die in den Beispielprojekten in den nächsten Abschnitten angewandt werden. Dennoch werden wir, um die Grundprinzipien von Codegenerierung verständlicher zu machen, eine Codegenerierungslösung zuerst komplett per Hand ohne jegliche interne oder externe Tools entwickeln.Zuerst definiert man ein Kreismodell in der statischen, öffentlichen Klasse CircleModel(Listing 1). Dann führt die Methode GenerateHtmlReport() die Transformation des Kreismodells in eine HTML-Seite aus (Listing 2) und liefert einen entsprechenden String-Ausdruck zurück, der in der Datei gespeichert und dann im Browser dargestellt wird.Listing 1: Kreis-Modell als statische C#-Klasse
namespace PolygonModels {<br/> <span class="hljs-keyword"> public</span><span class="hljs-keyword"> static</span> class CircleModel {<br/> // definitions<br/> <span class="hljs-keyword"> public</span><span class="hljs-built_in"> const </span>string Namespace = <span class="hljs-string">"PolygonSamples"</span>;<br/> <span class="hljs-keyword"> public</span><span class="hljs-built_in"> const </span>string Class = <span class="hljs-string">"SampleCircle"</span>;<br/> <span class="hljs-keyword"> public</span><span class="hljs-built_in"> const </span>string Types = <span class="hljs-string">"double,double"</span>;<br/> <span class="hljs-keyword"> public</span><span class="hljs-built_in"> const </span>string Fields = <span class="hljs-string">"radius,diameter"</span>;<br/> <span class="hljs-keyword"> public</span><span class="hljs-built_in"> const </span>string Properties = <br/> <span class="hljs-string">"Radius,Diameter"</span>;<br/> <span class="hljs-keyword"> public</span><span class="hljs-built_in"> const </span>string Values = <span class="hljs-string">"10,20"</span>;<br/> // calculations<br/> <span class="hljs-keyword"> public</span><span class="hljs-keyword"> static</span> string[] PropertiesValues = <br/> Values.Split(',');<br/> <span class="hljs-keyword"> public</span><span class="hljs-keyword"> static</span> string[] PropertiesNames = <br/> Properties.Split(',');<br/> <span class="hljs-keyword"> public</span><span class="hljs-keyword"> static</span><span class="hljs-built_in"> double </span>RadiusValue = <br/> Convert.ToDouble(PropertiesValues[0]);<br/> <span class="hljs-keyword"> public</span><span class="hljs-keyword"> static</span><span class="hljs-built_in"> double </span>DiameterValue = <br/> 2 * RadiusValue;<br/> <span class="hljs-keyword"> public</span><span class="hljs-keyword"> static</span><span class="hljs-built_in"> double </span>AreaValue = <br/> Math.PI * Math.Pow(RadiusValue, 2);<br/> <span class="hljs-keyword"> public</span><span class="hljs-keyword"> static</span><span class="hljs-built_in"> double </span>PerimeterValue = <br/> Math.PI * DiameterValue;<br/> }<br/>}
Listing 2: Transformation des Kreis-Modells als HTML-Seite
<span class="xml">static void Main(string[] args) {</span><br/><span class="xml"> // init</span><br/><span class="xml"> string gencodeReportText = "";</span><br/><span class="xml"> string fileHtmlReport = "PolygonReport.html";</span><br/><span class="xml"> // generate polygon report (HTML-code)</span><br/><span class="xml"> gencodeReportText = GenerateHtmlReport();</span><br/><span class="xml"> // write report to the file and show in browser</span><br/><span class="xml"> System.IO.File.WriteAllText(</span><br/><span class="xml"> fileHtmlReport, gencodeReportText);</span><br/><span class="xml"> System.Diagnostics.Process.Start(fileHtmlReport);</span><br/><span class="xml">}</span><br/><br/><span class="xml">private static string GenerateHtmlReport() {</span><br/><span class="xml"> string reportResult = "";</span><br/><span class="xml"> reportResult += "<span class="hljs-comment">&lt;!--Dieser Code wurde von </span></span><br/><span class="xml"><span class="hljs-comment"> einem Tool generiert. --&gt;</span>";</span><br/><span class="xml"> reportResult += $"<span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span> Report of </span><br/><span class="xml"> \"{CircleModel.Class}\"-Object <span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>";</span><br/><span class="xml"> reportResult += " <span class="hljs-tag">&lt;<span class="hljs-name">hr</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span> Properties:<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>";</span><br/><span class="xml"> reportResult += " <span class="hljs-tag">&lt;<span class="hljs-name">table</span>&gt;</span>";</span><br/><span class="xml"> for (int i = 0; </span><br/><span class="xml"> i <span class="hljs-tag">&lt; <span class="hljs-attr">CircleModel.PropertiesValues.Length</span>; <span class="hljs-attr">i</span>++)</span></span><br/><span class="xml"><span class="hljs-tag"> {</span></span><br/><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">reportResult</span> += <span class="hljs-string">" &lt;tr&gt;"</span>;</span></span><br/><span class="xml"><span class="hljs-tag"> <span class="hljs-attr">reportResult</span> += <span class="hljs-string">$</span>" &lt;<span class="hljs-attr">td</span>&gt;</span> </span><br/><span class="xml"> {CircleModel.PropertiesNames[i]} = <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>";</span><br/><span class="xml"> reportResult += $" <span class="hljs-tag">&lt;<span class="hljs-name">td</span>&gt;</span> </span><br/><span class="xml"> {CircleModel.PropertiesValues[i]} <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>";</span><br/><span class="xml"> reportResult += " <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>";</span><br/><span class="xml"> }</span><br/><span class="xml"> reportResult += "<span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>";</span><br/><span class="xml"> reportResult += "<span class="hljs-tag">&lt;<span class="hljs-name">hr</span>&gt;</span><span class="hljs-tag">&lt;<span class="hljs-name">h2</span>&gt;</span> Calculated Values:<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>";</span><br/><span class="xml"> reportResult += $" </span><br/><span class="xml"> Area = {CircleModel.AreaValue},";</span><br/><span class="xml"> reportResult += $" </span><br/><span class="xml"> Perimeter = {CircleModel.PerimeterValue}";</span><br/><span class="xml"> reportResult += "<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>";</span><br/><span class="xml"> return reportResult;</span><br/><span class="xml">}</span>
Beachten Sie die Zeichenfolgeninterpolation [7] (seit C# 6.0 verfügbar) bei der Texttransformation (Listing 2). Wenn eine Zeichenfolge vom Typ String mit einem $-Zeichen beginnt, kann sie die auszuwertenden Ausdrücke mit geschweiften Klammern einschließen, ohne die String.Format()-Funktion aufrufen zu müssen, zum Beispiel:
reportResult += $<span class="hljs-string">"<h1> Report of \"</span>
{CircleModel.<span class="hljs-keyword">Class</span>}\<span class="hljs-string">"-Object </h1>"</span><span class="hljs-comment">;</span>
Die für diesen Abschnitt relevanten Demo-Projekte sind GeometricModels und GenExampleDirect.
Sprachneutrale Codegenerierung mit .NET
Wenn man den Code in verschiedenen Programmiersprachen generieren möchte, kann dabei der im .NET Framework enthaltene Mechanismus namens Code Document Object Model (kurz CodeDOM) [8] helfen. CodeDOM baut eine Beschreibung in einem CodeCompileUnit-Objekt mit einer Sammlung der verknüpften Objekte auf, die Elemente des generierenden Codes repräsentieren – Namensräume, Klassen, Eigenschaften, Methoden et cetera. Solche verknüpften Objekte bilden eine Datenstruktur, die als CodeDOM-Diagramm bekannt ist und die Zielstruktur des zu generierenden Codes modelliert. Ein CodeDOM-Diagramm wird dann an den jeweiligen Codeprovider gebunden, der den Code für die gewählte Zielsprache generiert. Im .NET Framework stehen standardmäßig die Codeprovider für C#, JavaScript und Visual Basic zur Verfügung. Die Erstellung eines jeweiligen Providers kann wie folgt aussehen:<span class="hljs-keyword">var</span> csProvider = <span class="hljs-keyword">new</span> <span class="hljs-type">CSharpCodeProvider</span>();
<span class="hljs-keyword">var</span> csProvider2 =
CodeDomProvider.CreateProvider (<span class="hljs-string">"CSharp"</span>);
<span class="hljs-keyword">var</span> jsProvider =
CodeDomProvider.CreateProvider (<span class="hljs-string">"JScript"</span>);
<span class="hljs-keyword">var</span> vbProvider == CodeDomProvider.CreateProvider (<span class="hljs-string">"VB"</span>);
Die Liste von Codeprovidern kann auch von Entwicklern selbst erweitert werden, um die Unterstützung für weitere Programmiersprachen einzuführen.Im Namensraum System.CodeDom sind die Typen für die Programmiersprachen-neutralen Darstellungen der logischen Struktur des Quellcodes definiert, und im Namensraum System.CodeDom.Compiler wiederum sind die Typen enthalten, die zum Generieren von Quellcode von CodeDOM-Diagrammen und zum Verwalten der Quellcodekompilierung in unterstützten Sprachen verwendet werden können.Wenn man das Konsolenprogramm GenExampleCodeDOM ausführt, wird die Methode GenerateCodeInTargetProject() aufgerufen und die Datei Main.cs im Projekt TargetCodeDOM persistiert. Sie können dieses Projekt ausführen, um festzustellen, dass die Codegenerierung korrekt gelaufen ist. In Listing 3 sind der Konstruktor und die AddFields()-Methode der Codegenerierungsklasse CodeDOMGeneratorSample dargestellt. In den Listing 4 und Listing 5 kann man die Codegenerierungsergebnisse für die C#-und VB-Variante sehen. Dabei handelt es sich um Auszüge – den vollständigen Code finden Sie im Projekt GenExampleCodeDOM.
Listing 3: CodeDOM, Codegenerierungsklasse (Auszug)
// Define the class.<br/>public CodeDOMGeneratorSample() {<br/> targetUnit = <span class="hljs-keyword">new</span> CodeCompileUnit();<br/> CodeNamespace samples = <br/> <span class="hljs-keyword">new</span> CodeNamespace(<span class="hljs-string">"CodeDOMPolygonSample"</span>);<br/> samples.Imports.Add(<br/> <span class="hljs-keyword">new</span> CodeNamespaceImport(<span class="hljs-string">"System"</span>));<br/> targetClass = <br/> <span class="hljs-keyword">new</span> CodeTypeDeclaration(<span class="hljs-string">"CodeDOMRectangleClass"</span>);<br/> targetClass.IsClass = <span class="hljs-literal">true</span>;<br/> targetClass.TypeAttributes = <br/> TypeAttributes.Public | TypeAttributes.Sealed;<br/> samples.Types.Add(targetClass);<br/> targetUnit.Namespaces.Add(samples);<br/>}<br/><br/>// Adds two fields to the class.<br/>public void AddFields() {<br/> // <span class="hljs-keyword">Declare</span> the widthValue field.<br/> CodeMemberField widthValueField = <br/> <span class="hljs-keyword">new</span> CodeMemberField();<br/> widthValueField.Attributes = <br/> MemberAttributes.<span class="hljs-keyword">Private</span>;<br/> widthValueField.Name = <span class="hljs-string">"widthValue"</span>;<br/> widthValueField.<span class="hljs-keyword">Type</span> <span class="hljs-type">= </span><br/><span class="hljs-type"> </span><span class="hljs-keyword">new</span> CodeTypeReference(typeof(System.Double));<br/> widthValueField.Comments.Add(<br/> <span class="hljs-keyword">new</span> CodeCommentStatement(<br/> <span class="hljs-string">"The width of the object."</span>));<br/> targetClass.Members.Add(widthValueField);<br/><br/> // <span class="hljs-keyword">Declare</span> the heightValue field<br/> CodeMemberField heightValueField = <br/> <span class="hljs-keyword">new</span> CodeMemberField();<br/> heightValueField.Attributes = <br/> MemberAttributes.<span class="hljs-keyword">Private</span>;<br/> heightValueField.Name = <span class="hljs-string">"heightValue"</span>;<br/> heightValueField.<span class="hljs-keyword">Type</span> <span class="hljs-type">= </span><span class="hljs-keyword">new</span> CodeTypeReference(<br/> typeof(System.Double));<br/> heightValueField.Comments.Add(<br/> <span class="hljs-keyword">new</span> CodeCommentStatement(<br/> <span class="hljs-string">"The height of the object."</span>));<br/> targetClass.Members.Add(heightValueField);<br/>}
Listing 5: Generierte VB-Klasse
<span class="hljs-keyword">Namespace</span> CodeDOMPolygonSample<br/> <span class="hljs-keyword">Public</span> NotInheritable <span class="hljs-keyword">Class</span> CodeDOMRectangleClass<br/> <span class="hljs-string">'The width of the object</span><br/><span class="hljs-string"> Private widthValue As Double</span><br/><span class="hljs-string"> '</span>The height <span class="hljs-keyword">of</span> the object<br/> <span class="hljs-keyword">Private</span> heightValue <span class="hljs-keyword">As</span> Double<br/> <span class="hljs-keyword">End</span> Class<br/>End <span class="hljs-keyword">Namespace</span>
Listing 4: Generierte C#-Klasse
<span class="hljs-keyword">namespace</span> <span class="hljs-title">CodeDOMPolygonSample</span> {<br/> <span class="hljs-keyword">using</span> System;<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">sealed</span> <span class="hljs-keyword">class</span> <span class="hljs-title">CodeDOMRectangleClass</span> {<br/> <span class="hljs-comment">// The width of the object</span><br/><span class="hljs-comment"> private double widthValue;</span><br/><span class="hljs-comment"> // The height of the object</span><br/><span class="hljs-comment"> private double heightValue;</span><br/><span class="hljs-comment"> }</span><br/><span class="hljs-comment">}</span>
Trotz unbestrittener Vorteile wie zum Beispiel der Sprachunabhängigkeit weist die CodeDOM-Technologie auch gewisse Nachteile auf. Der allergrößte Nachteil wird offensichtlich, wenn man Listing 3 mit Listing 4 beziehungsweise Listing 5 vergleicht. Um ein Dutzend Codezeilen zu generieren, muss man dreimal so viel Generierungscode schreiben. Das ist alles andere als produktiv. Darum sollte man sich für CodeDOM nur entscheiden, wenn Sprachunabhängigkeit unbedingt gefordert und budgetiert ist. Zu den weiteren Nachteilen zählen die limitierte Unterstützung von Sprachkonstrukten sowie die fehlende Unterstützung von weiteren Sprachen wie zum Beispiel Python oder SQL.Die für dieses Abschnitt relevanten Demoprojekte sind GenExampleCodeDOM und TargetCodeDOM.
Vorlagenbasierte Codegenerierung mit .NET
Es gibt jedoch in .NET eine Technologie, die die Komplexität und den Entwicklungsaufwand einer Codegenerierungslösung deutlich reduziert: das Text Template Transformation Toolkit oder kurz T4 [9].Anstatt ein Programm zu schreiben, welches das andere Programm komplett implementiert, nutzt man hier eine Textvorlage, die fast wie die auszugebende Datei aussieht. Nur an den variablen (vom Geschäftsmodell abhängigen) Stellen wird Generierungscode injiziert. In einer T4-Vorlage öffnet ein <#-Zeichen einen Codegenerierungsblock. Mit #> schließt man ihn wieder. Der übrige Text wird eins zu eins in die Ausgabedatei geschrieben. Der vorliegende Artikel wird nicht die Einzelheiten der T4-Technologie erläutern, möchte aber auf einen möglichen Einstiegspunkt [10] hinweisen, der beim Schreiben von T4-Textvorlagen behilflich sein kann.T4-Codegenerierung findet auch in .NET und Visual Studio rege Verwendung – unter anderem im .NET Entity Framework [11]. Es gibt zwei Arten von T4-Textvorlagen, die zur Entwurfszeit (Designtime) [12] oder zur Laufzeit (Runtime) [13] ausgeführt werden. In einem Visual-Studio-Projekt legt das die Eigenschaft Benutzerdefiniertes Tool fest.- TextTemplatingFileGenerator: zur Entwurfszeit
- TextTemplatingFilePreprocessor: zur Laufzeit
Generierung mit T4-Vorlagen zur Entwurfszeit
Zuerst werden wir eine Lösung (siehe GenExampleT4-Projekt) mit Entwurfszeit-Textvorlagen [12] entwickeln. Dafür fügt man zu einem Projekt ein neues Element hinzu, zum Beispiel Main.tt. Alle T4-Textvorlagendateien haben immer die Erweiterung .tt. Die neu erstellte Textvorlage referenziert ein Rechteck-Modell (Listing 6). Dafür wird die T4-Direktive assembly verwendet:Listing 6: Rechteck-Modell (statische C#-Klasse)
<span class="hljs-keyword">namespace</span> <span class="hljs-title">FlatModels</span> {<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">class</span> <span class="hljs-title">RectangleModel</span> {<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> Namespace = <br/> <span class="hljs-string">"PolygonSamples"</span>;<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> Class = <span class="hljs-string">"SampleRectangle"</span>;<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> Types = <span class="hljs-string">"double,double"</span>;<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> Fields = <span class="hljs-string">"width,height"</span>;<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> Properties = <span class="hljs-string">"Width,Height"</span>;<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> InitialValues = <br/> <span class="hljs-string">"15.0D,10.0D"</span>;<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> AreaFormula = <br/> <span class="hljs-string">"Width * Height"</span>;<br/> }<br/>}
<#@ assembly name="$(SolutionDir)GeometricModelsCore
<span class="hljs-symbol">\b</span>in<span class="hljs-symbol">\D</span>ebug<span class="hljs-symbol">\n</span>etcoreapp3.1<span class="hljs-symbol">\G</span>eometricModelsCore.dll" #>
Sonstige Umsetzungseinzelheiten kann man in der Datei Main.tt nachschlagen oder sich einen Auszug davon im Listing 7 ansehen.
Listing 7: Rechteck Textvorlage
&lt;#@ template debug=<span class="hljs-string">"false"</span> hostspecific=<span class="hljs-string">"false"</span> <br/> language=<span class="hljs-string">"C#"</span> #&gt;<br/>&lt;#@ assembly name=<span class="hljs-string">"$(SolutionDir)GeometricModelsCore</span><br/><span class="hljs-string"> \bin\Debug\netcoreapp3.1\GeometricModelsCore.dll"</span> #&gt;<br/>&lt;#@ <span class="hljs-keyword">import</span> namespace=<span class="hljs-string">"FlatModels"</span> #&gt;<br/>&lt;#@ output extension=<span class="hljs-string">".cs"</span> #&gt;<br/><br/>namespace &lt;#= RectangleModel.Namespace #&gt; {<br/> public sealed <span class="hljs-keyword">class</span> &lt;#= RectangleModel.Class #&gt; {<br/> &lt;#<br/> var types = RectangleModel.Types.Split(<span class="hljs-string">','</span>);<br/> var props = RectangleModel.Properties<br/> .Split(<span class="hljs-string">','</span>);<br/> var fields = RectangleModel.Fields.Split(<span class="hljs-string">','</span>);<br/> var i = <span class="hljs-number">0</span>;<br/> foreach (var field <span class="hljs-keyword">in</span> fields)<br/> {#&gt;<br/> <span class="hljs-comment">// The &lt;#=field#&gt; of the object</span><br/><span class="hljs-comment"> private &lt;#=types[i]#&gt; &lt;#=field#&gt;Value;</span><br/><span class="hljs-comment"> // The &lt;#=props[i]#&gt; property for the object</span><br/><span class="hljs-comment"> public &lt;#=types[i]#&gt; &lt;#=props[i]#&gt; </span><br/><span class="hljs-comment"> { get { return this.&lt;#=field#&gt;Value; } }</span><br/><span class="hljs-comment"> &lt;# // next model member</span><br/><span class="hljs-comment"> i++; </span><br/><span class="hljs-comment"> }#&gt;</span><br/><span class="hljs-comment"> }</span><br/><span class="hljs-comment">}</span>
In dem Moment, wenn Sie die Textvorlage speichern, erzeugt Visual Studio eine Ergebnisdatei und verknüpft sie mit Ihrer Vorlage im VS-Projekt. Deswegen ist es wichtig, dass das Geschäftsmodell bereits zur Entwurfszeit im Codegenerierungskontext zur Verfügung steht. Das funktioniert auch mit der statischen, öffentlichen Modellklasse RectangleModel aus der referenzierten Assembly GeometricModelsCore.dll wunderbar.Im Listing 8 ist eine solche Ergebnisdatei Main.cs dargestellt, die dem Auszug in Listing 7 entspricht.
Listing 8: Rechteck C#-Klasse
<span class="hljs-keyword">namespace</span> <span class="hljs-title">PolygonSamples</span> {<br/> <span class="hljs-keyword">using</span> System;<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">sealed</span> <span class="hljs-keyword">class</span> <span class="hljs-title">SampleRectangle</span> { <br/> <span class="hljs-comment">// The width of the object</span><br/><span class="hljs-comment"> private double widthValue;</span><br/><span class="hljs-comment"> // The height of the object</span><br/><span class="hljs-comment"> private double heightValue;</span><br/><span class="hljs-comment"> }</span><br/><span class="hljs-comment">} </span>
Anhand der Main.tt-Textvorlage wird ein vollständiger C#-Code in der Datei Main.cs generiert, der sofort ausgeführt werden kann. Das Programm gibt dabei die Eingabeparameter – Breite und Höhe und die Fläche des im Geschäftsmodell definierten Rechtecks auf der Konsole aus. Das für dieses Abschnitt relevante Demo-Projekt ist GenExampleT4.
Generierung mit T4-Textvorlagen zur Laufzeit
Was können Sie machen, wenn die Geschäftsmodelldaten für generierte Inhalte nicht zur Entwurf-, sondern erst zur Laufzeit zu Verfügung stehen und/oder die Codegenerierungsausgabe nicht in eine Projektdatei von Visual Studio, sondern in eine zur Entwurfszeit nicht greifbare Datei beziehungsweise einen Datenstrom stattfinden soll?Dafür kann man eine andere Art der T4-Textvorlagen verwenden – die Laufzeittextvorlagen. Der Trick dabei besteht darin, dass die Laufzeittextvorlage von einem anderen Werkzeug verarbeitet wird: dem TextTemplatingFilePreprocessor. Dabei wird statt der Ergebnisdatei selbst ein Programm generiert, das ein anderes Programm beziehungsweise eine Textausgabe schreibt (Listing 9). Für Demozwecke haben wir noch eine weitere Laufzeittextvorlage hinzugefügt: MainReport.tt(Listing 10), die eine weitere HTML-Datei aus demselben TrapezoidModel generiert.Listing 9: C#-Code (Auszug, generiert von Main.tt)
<span class="hljs-keyword">namespace</span> <span class="hljs-title">GenExampleT4Dynamic</span> {<br/> <span class="hljs-keyword">using</span> FlatModels;<br/> <span class="hljs-keyword">using</span> System;<br/> ... <br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">partial</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> : <span class="hljs-title">MainBase</span> {<br/> ...<br/> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">virtual</span> <span class="hljs-keyword">string</span> <span class="hljs-title">TransformText</span>(<span class="hljs-params"></span>) </span>{<br/> ...<br/> <span class="hljs-keyword">this</span>.Write(<span class="hljs-keyword">this</span>.ToStringHelper<br/> .ToStringWithCulture(<br/> TrapezoidModel.Namespace));<br/> <span class="hljs-keyword">this</span>.Write(<span class="hljs-string">"\r\n{\r\n using System; </span><br/><span class="hljs-string"> \r\n \r\n public sealed class "</span>);<br/> <span class="hljs-keyword">this</span>.Write(<span class="hljs-keyword">this</span>.ToStringHelper<br/> .ToStringWithCulture(TrapezoidModel.Class));<br/> <span class="hljs-keyword">this</span>.Write(<span class="hljs-string">"\r\n { "</span>);<br/><br/> <span class="hljs-keyword">var</span> types = TrapezoidModel.Types.Split(<span class="hljs-string">','</span>);<br/> <span class="hljs-keyword">var</span> props = TrapezoidModel.Properties<br/> .Split(<span class="hljs-string">','</span>);<br/> <span class="hljs-keyword">var</span> fields = TrapezoidModel.Fields.Split(<span class="hljs-string">','</span>);<br/> ...<br/> }<br/> }<br/>}
Der Codegenerierungs- und Testprozess mit Laufzeittextvorlagen ist in Listing 11 dargestellt:
Listing 11: Codegenerierung mit Laufzeit-Textvorlagen – Program.cs
<span class="hljs-keyword">class</span> Program {<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> OutputFileName = <span class="hljs-string">"Main.cs"</span>;<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> TargetProject = <br/> <span class="hljs-string">"TargetRuntimeT4"</span>;<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> OutputFileOfTargetProject {<br/> <span class="hljs-built_in">get</span> { <span class="hljs-built_in">return</span> $@<span class="hljs-string">"..\..\..\{TargetProject}</span><br/><span class="hljs-string"> \{OutputFileName}"</span>; }<br/> }<br/> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">string</span> ReportFilename <br/> { <span class="hljs-built_in">get</span> { <span class="hljs-built_in">return</span> <span class="hljs-string">"MainReport.html"</span>; } }<br/><br/> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> Main(<span class="hljs-keyword">string</span>[] args) {<br/> var gencodeMain = <span class="hljs-keyword">new</span> Main();<br/> var gencodeReport = <span class="hljs-keyword">new</span> MainReport();<br/> <span class="hljs-keyword">String</span> gencodeMainText = <br/> gencodeMain.TransformText();<br/> <span class="hljs-keyword">String</span> gencodeReportText = <br/> gencodeReport.TransformText();<br/> System.IO.<span class="hljs-built_in">File</span>.WriteAllText(<br/> OutputFileOfTargetProject, gencodeMainText);<br/> System.IO.<span class="hljs-built_in">File</span>.WriteAllText(<br/> ReportFilename, gencodeReportText);<br/><br/> <span class="hljs-built_in">Console</span>.WriteLine(<span class="hljs-string">"Generated file: "</span> + <br/> OutputFileOfTargetProject);<br/> System.Diagnostics.<span class="hljs-built_in">Process</span>.Start(ReportFilename);<br/> }<br/>}
- Man instanziert zwei Codegenerierungsobjekte gencodeMain und gencodeReport, deren Klassen wiederum aus den Laufzeittextvorlagen Main.tt und MainReport.tt generiert worden sind.
- Man ruft die jeweilige Funktion TransformText() auf und speichert die generierten Ergebnisse in den lokalen String-Variablen.
- Die Ergebnisse werden jeweils als Main.cs in das Zielprojekt und als MainReport.html in das aktuelle Laufzeitverzeichnis geschrieben.
- Ein Browser zeigt den generierten HTML-Bericht im Browser an.
- Um die Korrektheit der generierten Datei Main.cs zu überprüfen, führt man das Programm vom Zielprojekt (TargetRuntimeT4) aus.
Agile Codegenerierung mit Microsoft Excel
Stellen Sie sich vor, Sie müssen ein neues Feld zu einer Tabelle einer relationalen Datenbank hinzufügen. Eigentlich ist das keine komplizierte Aufgabe, und sie könnte durch die Ausführung eines DDL-Skripts mit folgendem Muster gelöst werden:ALTER TABLE <span class="hljs-meta">[<db-name>]</span>.<span class="hljs-meta">[<schema-name>]</span>.<span class="hljs-meta">[<table-name>]</span>
ADD <span class="hljs-meta">[<column-name>]</span> nvarchar(<span class="hljs-number">255</span>) NULL
Wie aber sieht es aus, wenn statt einer zehn, 100 oder 1000 Tabellen aktualisiert werden müssen? Anstatt viele Male die Zwischenablage zu beanspruchen, kann die Codegenerierung hier gute Dienste leisten.Man kann wie bereits beschrieben ein Modell mit den betroffenen Tabellen entwerfen und eine Codegenerierungslösung entwickeln, zum Beispiel mit CodeDOM- oder T4-Technik.Dieser Vorgang wiederum bedeutet aber einen gewissen Aufwand. Was kann man tun, wenn es schnell und trotzdem modellgetrieben gehen soll und/oder Visual Studio mit .NET beziehungsweise Programmierkenntnisse fehlen?Die Lösung für diese Anforderung lautet ganz schlicht und einfach: Wir verwenden ein Tabellenkalkulationsprogramm wie etwa Microsoft Excel.Zum Beispiel lässt sich die oben beschriebene Aufgabe für eine SQL-Server-Datenbank wie folgt lösen:
- Fragen Sie zuerst mithilfe der folgenden Zeilen alle nötigen Daten aus dem Informationsschema ab:
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> INFORMATION_SCHEMA.TABLES
<span class="hljs-keyword">where</span> table_type = <span class="hljs-string">'base table'</span>
<span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> table_name
<span class="hljs-keyword">GO</span>
- Fügen Sie diese Daten zu einem Modellblatt hinzu (siehe Bild 3 und Tables-Model-Blatt in ExcelSqlCodeGen.xlsx).

Tabellen-Modellvon Northwind-Datenbank(Bild 3)
Autor
- Erstellen Sie nun ein Codegeneratorblatt und schreiben Sie dort eine Excel-Formel für den benötigten SQL-Ausdruck hinein. Sehen Sie dazu Bild 4 und das CodeGen-AddColumn-Blatt.

Generierungdes DDL-Skripts für erste Tabelle(Bild 4)
Autor
- Im Codegeneratorblatt kopieren Sie die neuen Datensätze mit [Strg]+[C]/[Strg]+ [V] so lange, bis Sie alle Tabellen aus dem Modellblatt referenziert haben (vergleichen Sie dazu Bild 5). Dabei werden entsprechende Tabellennamen von Excel automatisch aktualisiert.

Generierungdes DDL-Skripts für alle Tabellen(Bild 5)
Autor
- Im Blatt CodeGen-DeleteColumn ist analog ein Szenario umgesetzt, in dem ein Feld aus allen vorhandenen Tabellen gelöscht wird.

C#-Klassengenerierungfür das Rechteck-Modell(Bild 6)
Autor
- Man definiert ein Polygon-Metamodell (siehe Blatt Meta-Modell und Tabelle 1).
- Im Rahmen des Polygon-Metamodells erstellen Sie ein Rechteckmodell (siehe Blatt Model-Rectangle) und setzen dort die Werte für die Breite und Höhe des Rechtecks fest.
- Im Blatt CodeGenerator-Rectangle wählen Sie den Modellnamen (B3 in Bild 6).
- Wählen Sie die Zellen C3:F3 aus und kopieren Sie diese mit dem VBA-Makro MacroInsertCellCopy ([Strg]+[Umschalt] +[G], siehe Listing 12) so lange, bis vom referenzierten Modell (Blatt Model-Rectangle) keine Daten mehr ankommen. Die eventuell zu viel kopierten Zellen (ohne Modelldaten) kann man gegebenenfalls auswählen und durch das Drücken von [Strg]+[Umschalt]+[D] mit MacroDeleteSelectedCells-Makrolöschen (siehe Listing 12). Als Ergebnis werden die Modelldaten in den Bereichen B3, B6, B9, B12 sowie dem Bereich C3:F4 (für Kreis- und Trapez-Modell jeweils C3:F3 und C3:F13) referenziert.
Listing 12: Zelleninhalte kopieren/löschen (VBA-Makro)
<span class="hljs-string">'----------------------------------------------</span><br/><span class="hljs-string">Sub MacroInsertCellCopy()</span><br/><span class="hljs-string">'</span> Ctrl+Shift+G<br/><span class="hljs-string">'----------------------------------------------</span><br/><span class="hljs-string"> Selection.Copy</span><br/><span class="hljs-string"> Selection.Offset(1, 0).Select</span><br/><span class="hljs-string"> Selection.Insert Shift:=xlDown</span><br/><span class="hljs-string">End Sub</span><br/><br/><span class="hljs-string">'</span>----------------------------------------------<br/>Sub MacroDeleteSelectedCells()<br/><span class="hljs-string">' Ctrl+Shift+D</span><br/><span class="hljs-string">'</span>----------------------------------------------<br/> Application<span class="hljs-selector-class">.CutCopyMode</span> = False<br/> Selection<span class="hljs-selector-class">.Delete</span> Shift:=xlUp<br/>End Sub
- Die einzelnen Referenzen (hellrot) in der Codegenerierungsvorlage (siehe Spalte A im Blatt CodeGenerator-Rectangle und in Bild 6) sowie die ersten Zeilen der Eingabeeigenschaften (gelb) und Ergebniseigenschaften (hellgrün) werden automatisch zugewiesen. Weitere Exemplare von Eingabe-/Ergebnis-Eigenschaften können analog wie im letzten Schritt hinzugefügt werden ([Strg]+[Umschalt]+[G]).
- Der generierte Code (A2:A49) wird manuell oder mit dem VBA-Makro CopyGenCodeToClipboard ([Strg]+[Umschalt] +[C], siehe Listing 13) in die Zwischenablage kopiert.
Listing 13: Generierten Code in die Zwischenablage kopieren
<span class="hljs-string">'----------------------------------------------</span><br/><span class="hljs-string">Public Sub CopyGenCodeToClipboard()</span><br/><span class="hljs-string">'</span> Ctrl+Shift+<span class="hljs-keyword">C</span><br/><span class="hljs-string">'----------------------------------------------</span><br/><span class="hljs-string"> '</span> <span class="hljs-keyword">Find</span> the last row of code<br/> Dim lastCodeRow As <span class="hljs-keyword">Integer</span><br/> lastCodeRow = <span class="hljs-keyword">Cells</span>(Rows.<span class="hljs-keyword">Count</span>, <span class="hljs-number">1</span>).<span class="hljs-keyword">End</span>(xlUp).<span class="hljs-keyword">Row</span><br/> <span class="hljs-string">' Copy the generated code from the </span><br/><span class="hljs-string"> '</span> current sheet to the clipboard<br/> <span class="hljs-keyword">Range</span>(<span class="hljs-string">"A2:A"</span> &amp; CStr(lastCodeRow)).Copy<br/> <span class="hljs-string">' Say OK-Message</span><br/><span class="hljs-string"> MsgBox CStr(lastCodeRow - 1) _</span><br/><span class="hljs-string"> &amp; " of the generated Code have been </span><br/><span class="hljs-string"> copied to the clipboard!"</span><br/><span class="hljs-string">End Sub</span>
- Der generierte Code wird aus der Zwischenablage in die Datei Program.cs von Zielprojekt PolygonTestRectangle übertragen.
- Das Zielprojekt PolygonTestRectangle wird übersetzt und ausgeführt, um zu testen, dass alles erwartungsgemäß verlaufen ist.
Fazit
Mit modellgetriebenen Methoden mit Codegenerierung lassen sich in der Regel höhere Produktivität und Agilität bei der Softwareentwicklung erreichen. Ferner werden dadurch die Qualität, Stabilität und die Einheitlichkeit des generierten Codes deutlich erhöht.Visual Studio und das .NET Framework bieten diverse Techniken für die Codegenerierung an wie zum Beispiel CodeDOM oder T4. Es gibt außerdem jede Menge freie und kommerzielle Werkzeuge, die Modellierung und Codegenerierung unterstützen [14] [15]. Ferner ist die Codegenerierung mit XSL-Transformation [16] oder sogar als Ad-hoc-Szenario mit Tabellenkalkulationsprogrammen wie Microsoft Excel machbar.Die Codegenerierungslösung ist meistens nicht komplexer als andere Programmierlösungen. Die Codegenerierungslösung kann entweder als fertiges Tool bezogen oder (fast) von jedem bei Bedarf entwickelt werden. Modellgetriebene Methodik mit Codegenerierung sollte man überall anwenden, wo sie Sinn ergibt und den gegebenen Projektanforderungen nicht widerspricht (zum Beispiel kein Budget für eine Codegenerierungslösung).Der Autor ermuntert die Leser dazu, mit den Demoprojekten [2] zu experimentieren.Fussnoten
- Mykola Dobrochynskyy, Auf Knopfdruck Code, dotnetpro 1/2022, Seite 80 ff., http://www.dotnetpro.de/A2201CodeGeneration
- Demo-Projektmappe zum Artikel „Agil Code generieren“, http://www.dotnetpro.de/SL2202CodeGeneration1
- Peter Vogel, Practical Code Generation in .NET, Addison-Wesley, 2010, ISBN 978-0-321-60678-5,
- Metamodell, http://www.dotnetpro.de/SL2202CodeGeneration2
- Document Object Model – DOM, http://www.dotnetpro.de/SL2202CodeGeneration3
- Abstract Syntax Tree – AST, http://www.dotnetpro.de/SL2202CodeGeneration4
- Zeichenfolgeninterpolation (C#-Referenz), http://www.dotnetpro.de/SL2202CodeGeneration5
- Code Document-Object-Model (CodeDOM), http://www.dotnetpro.de/SL2202CodeGeneration6
- T4 – Text Template Transformation Toolkit, http://www.dotnetpro.de/SL2202CodeGeneration7
- Schreiben einer T4-Vorlage, http://www.dotnetpro.de/SL2202CodeGeneration8
- Entity Framework 6, Designer-Vorlagen für die Codegenerierung, http://www.dotnetpro.de/SL2202CodeGeneration9
- Codegenerierung mit T4-Textvorlagen zur Entwurfszeit, http://www.dotnetpro.de/SL2202CodeGeneration10
- Codegenerierung mit T4-Textvorlagen zur Laufzeit, http://www.dotnetpro.de/SL2202CodeGeneration11
- Codegenerierungstools, http://www.dotnetpro.de/SL2202CodeGeneration12
- UML-Tools, http://www.dotnetpro.de/SL2202CodeGeneration13
- XSL Transformation, http://www.dotnetpro.de/SL2202CodeGeneration14