Month List

RecentComments

Comment RSS

Code Generation with T4 Templates – A must have for developers

by Ioannis 26. October 2010 20:09

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

Code Snippets in Visual Studio

by Ioannis 6. April 2009 08:02

Code snippets in Visual Studio are small parts of written code that can be inserted using a specific shortcut again and again in applications. In this post I will demonstrate how to use them through an example.

Suppose you find yourself writing the code below again and again in your application:

if (SomeObject!=null)
    Do this
else
    Do the other

Then it is nice if you could create a snippet in visual studio and whenever you need this code, insert it automatically through a sequence of keyboard/mouse shortcuts.

To achieve this create an xml file called NewSnippet.snippet using your favorite text editor and add the following:

 <CodeSnippets
    xmlns="http://schemas.microsoft.com/VisualStudio/2008/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>
                INSERT YOUR SNIPPET TITLE HERE
            </Title>
        </Header>
        <Snippet>
            <Code Language="(SNIPPED'S CODE LANGUAGE, EG:CSharp)"> 
                <![CDATA[

                INSERT YOUR CODE HERE

                ]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

Where in the "INSERT CODE HERE" section you put the previous code that you want automatically generated.

Save this file and within Visual Studio under Tools/Code Snippet Manager you can "Import"  the snippet to the desired folder (in this dialog you can actually do more than that in terms of organizing your snippets).

After importing your snippet you can press the shortkut Ctrl+K+X in your code and have your snippet inserted.

This is half of the power. The other half comes from the fact that you can insert placeholders in your snippet for specific words in the snippet's code that should be changed according to the context the snippet is inserted. For example you want the following code in a snippet:

private String TestMe="";
TestMe=GetInfo();
if (TestMe=="") //Do Something;

But whenever you use it the property type should be different as well as with the property name and the initializer.

You can tell in your snippet where the user should be prompted to enter a value and where this value should be entered. The snippet's code for this is as follows (note the declaration section and its use within the code and the correspondence between the ID for each declaration and the enclosed IDnames in $ signs within the snippet's code):

<CodeSnippets
    xmlns="http://schemas.microsoft.com/VisualStudio/2008/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>
                Test Snippet
            </Title>
        </Header>
        <Snippet>
        <Declarations>
                <Literal>
                    <ID>InitializationValue</ID>
                    <ToolTip>Put initialization here.</ToolTip>
                    <Default>""</Default>
                </Literal>
                <Literal>
                    <ID>PropertyName</ID>
                    <ToolTip>Put property name.</ToolTip>
                    <Default>TestMe</Default>
                </Literal>
                <Object>
                    <ID>PropertyType</ID>
                    <ToolTip>Replace with the desired type.</ToolTip>
                    <Default>String</Default>
                </Object>
            </Declarations>
       
            <Code Language="CSharp">
                <![CDATA[
                private $PropertyType$ $PropertyName$=$InitializationValue$;
                $PropertyName$=GetInfo();
                if ($PropertyName$==$InitializationValue$) //Do Something;
                ]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

The following if imported as snippet in Visual Studio appears with Ctrl+K+X and as soon as it appears we can use the Tab key to naigate in the snippet's paramters and set their values accordingly.

kick it on DotNetKicks.com

Powered by BlogEngine.NET 2.0.0.36

ITPRO DevConnections 2011

Creative Commons License

Programming Blogs - BlogCatalog Blog Directory Add to Technorati Favorites

MVP Award


Ioannis Panagopoulos





This blog is using BlogEngine.Net and is hosted in the hoster below. I have not experienced any problems installing BlogEngine.Net in the host and I am satisfied with the host's response times. Therefore I recommend it.


DiscountASP Add