Ioannis Panagopoulos blog

Tutorials on HTML5, Javascript, WinRT and .NET

Code Generation with T4 Templates – A must have for developers

by Ioannis Panagopoulos

T4 Templates files have a .tt extension and are the input to VS2010’s code generation engine for automatically generating text files with all sorts of content (C# code, SQL Commands, XAML, HTML, XML etc). In this post, we will briefly see how we can create and use them and examine some very useful scenarios on situations where they may come in handy. Before starting with the tutorial it is recommended that you download the T4 Editor. It provides syntax highlighting when editing a .tt file from within VS 2010. If you do not care about syntax highlighting you can go on without it.

 

T4 files are easily generated by adding a “Text Template” from the Add/New Item dialog:

 

image 

Code Execution

Within the .tt file you write C# enclosed in <# #> tags which is executed by the engine, while everything that is not enclosed within those tags gets printed on the output file. For example the following code:

 

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
<# for (int i=0;i<10;i++) 
   {
#>
Hello World (<#=i#>)
<#}#>

(HelloWorld.tt)

 

Says that we are using C# for code, the output file extension will be .txt and we want to print 10 times to the output file the sentence “Hello World” followed by the index in parentheses. Note that carriage returns and empty lines, tabs and spaces are transferred as they appear in the .tt file to the output file. So to avoid any extra spaces the “<#” tags must always begin at the first column of the row unless there is a specific intension to do it otherwise. Also note that the <#= #> tags instruct the processor to directly print to the output whatever is inside.

Now lets move to the following example:

Namespace Importing

If we want to omit the namespace definition from a specific class, for example write List<int> instead of System.Collections.Generic.List<int> we can import once the namespace by using the import directive. The following example generates 100 random numbers and stores them in a list. Not the <#@import namespace … #> directive at the beginning.

 

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ import namespace="System.Collections.Generic"#>
<#@ output extension=".txt" #>
<# 
    List<int> IntegersList=new List<int>();
    Random Rand=new Random();
    for (int i=0;i<100;i++)
    {
        int num=Rand.Next(0,1000);
        IntegersList.Add(num);
#>
<#=    num#>,
<#  }#>

(RandGenerator.tt)

Multiple output file generation

The simplest approach for writing to multiple files is to use the classes in the “System.IO” namespace to generate the files. For example look at  the following code:

 

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ import namespace="System.IO"#>
<#
       string T4TemplatePath = Path.GetDirectoryName(Host.TemplateFile);
#>
THIS WILL BE WRITTEN TO FILE (ManyOutputsSimple1.txt)
<#
    File.WriteAllText(T4TemplatePath+"\\ManyOutputsSimple1.txt", this.GenerationEnvironment.ToString());
    this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length);
#>
THIS WILL BE WRITTEN TO FILE (ManyOutputsSimple2.txt)
<#
    File.WriteAllText(T4TemplatePath+"\\ManyOutputsSimple2.txt", this.GenerationEnvironment.ToString());
    this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length);
#>

(ManyOutputsSimple.tt)

This code generates two files and writes one sentence to each one of them. There are quite a few things to note here. First the fact that the hostspecific attribute is set to “true”. This allows us to use the Host object which has a reference to the generator’s engine. This object allows us to get the directory path the template resides. Second, the reference to the GenerationEnvironment object. This object holds everything that will be written to the output file. This is why we write to each file the contents of its ToString() method. After writing the data to the first file, we clear its contents in order to start writing the new data to the second file.

This approach of multiple file generation is the simplest one but does not have this nice feature of adding the generated items within the Visual Studio project, just under the generator template. The easiest way to also have this feature is to download the T4 Toolbox and use the methods provided there as follows:

 

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ include file="T4Toolbox.tt" #>
<#
  Test1Template t = new Test1Template();
  t.Output.File = "Test1.txt";
  t.Render();
#>

<#+
public class Test1Template : Template
{
    public override string TransformText()
    {#>
This is a Test1
<#+ return this.GenerationEnvironment.ToString();
    }
}
#>

(ManyOutputsExtended.tt)

Note that class definitions are encloses within <#+ #> tags. Also not the inclusion of the “T4Toolbox.tt” file and the way it is used. Finally, you will see that in Visual Studion the generated Test1.txt file is added in the project below the .tt file.

 

Having those in mind lets see some typical uses for T4 Templates:

Code Generation from MS SQL Database

A simple template that generates POCO classes from the tables of an MSSQL database is as follows (the database in the example is called “UAnswer”):

 

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
<#@ assembly name="System.Xml"#>
<#@ assembly name="Microsoft.SqlServer.Smo, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" #>
<#@ assembly name="Microsoft.SqlServer.ConnectionInfo, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" #>
<#@ assembly name="Microsoft.SqlServer.Management.Sdk.Sfc, Version=10.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" #>
<#@ import namespace="Microsoft.SqlServer.Management.Smo" #>
<#@ import namespace="System.Collections.Generic" #>
using System;
<#    Dictionary <string,string> DataTypes=new Dictionary<string,string>()
    {
        {"varbinary","Byte[]"},    {"binary","Byte[]"},{"text","String"},
        {"bit","boolean"},{"int","int"},{"bigint","long"},
        {"nvarchar","String"},{"smallint","Byte"},{"datetime","DateTime"}
    };
    Server server=new Server();
    Database database=server.Databases["UAnswer"];
    foreach (Table tab in database.Tables)
    {
        if (tab.Name!="sysdiagrams") 
        {#>
class <#= tab.Name.ToString()#> 
{ 
<#             foreach (Column col in tab.Columns)
            {#>
    public <#=DataTypes[col.DataType.ToString()]#> <#=col.Name#> {get; set;}
<#             }#>
}

<#         }
    }
#>

(SQLDBCodeGeneration.tt)

 

XAML Code generation from a class through reflection

The following example generates the grid columns for a GridView in XAML by reading the properties of a class in a dll (in the example the dll is named “TestLib” and the class is named “TestClass”):

 

<#@ template language="C#" hostspecific="True" debug="True" #>
<#@ output extension="xaml" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Reflection"#>
<#@ import namespace="System.Collections.Generic" #>
<GridView>
<#  string T4TemplatePath = Path.GetDirectoryName(Host.TemplateFile);
    Assembly a=Assembly.LoadFile(T4TemplatePath+@"\..\TestLib\bin\Debug\TestLib.dll"); 
    foreach (Type t in a.GetTypes())
        if (t.Name=="TestClass") {
                foreach (PropertyInfo Prop in t.GetProperties())
                    if (Prop.CanWrite) {
#>
    <GridViewColumn Width="110" DisplayMemberBinding="{Binding <#=Prop.Name #>}">
        <GridViewColumnHeader Content="<#=Prop.Name #>"/>
    </GridViewColumn>
<#                    }
        }
#>
</GridView>

(XAMLGenerator.tt)

This concludes our brief introduction to T4 Templates. The VS project containing all those templates is inside the

. If you would like a more thorough introduction to T4 Templates I highly recommend you read the articles by Oleg Sych .

Shout it

blog comments powered by Disqus
hire me