generated fi le this shouldn ’ t be an issue.
Creating a T4 Template . 265
266 .
chaPter 14 code generATion WiTh T4
At the bottom of the template file add a single line containing the words Hello World and save the
template.
c#
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
Hello World
Code snippet HelloWorld.tt
Vb
<#@ template debug="false" hostspecific="false" language="VB" #>
<#@ output extension=".txt" #>
Hello World
Code snippet HelloWorld.tt
As was mentioned previously, templates are run every time they are saved, so the generated file will
be updated with the new contents of the template. Open up the generated file and you will see the
text Hello World in there.
Although each individual template file can always be regenerated by
opening it and saving it again, Visual Studio also has a button at the top
of the Solution Explorer tool window to Transform All Templates (see
fiGure 14-4
Figure 14-4). Clicking this button transforms all of the templates in the
solution.
As was mentioned previously, if the output directive specifies an extension that matches the
language of the current project, the resulting generated file is included in the project. You will get
full IntelliSense from types and members declared within generated files. The next code snippet
shows a T4 template along with the code that it generates. The generated class can be accessed by
other parts of the program and a small console application demonstrating this follows.
c#
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
namespace AdventureWorks {
class GreetingManager {
public static void SayHi() {
System.Console.WriteLine("Aloha Cousin!");
}
}
}
Code snippet GreetingManager.tt
namespace AdventureWorks {
class GreetingManager {
public static void SayHi() {
Creating a T4 Template .
267
System.Console.WriteLine("Aloha Cousin!");
}
}
}
Code snippet GreetingManager.cs
namespace AdventureWorks {
class Program {
static void Main(string[] args) {
GreetingManager.SayHi();
}
}
}
Code snippet Program.cs
Vb
< #@ template debug="false" hostspecific="false" language="VB" # >
< #@ output extension=".vb" # >
Public Class GreetingManager
Public Shared Sub SayHi
System.Console.WriteLine( "Aloha Cousin!" )
End Sub
End Class
Code snippet GreetingManager.tt
Public Class GreetingManager
Public Shared Sub SayHi()
System.Console.WriteLine("Aloha Cousin!")
End Sub
End Class
Code snippet GreetingManager.vb
Module Module1
Sub Main()
GreetingManager.SayHi()
End Sub
End Module
Code snippet Module1.vb
Although the rest of your application will get IntelliSense covering your
generated code, the T4 template files themselves have no IntelliSense or syntax
highlighting in Visual Studio 2010. A few third-party editors and plug-ins are
available that provide a richer design-time experience for T4.
268 .
chaPter 14 code generATion WiTh T4
This example works, but it doesn’t really demonstrate the power and flexibility that T4 can offer.
This is because the template is completely static. To create useful templates, more dynamic capabilities
are required.
t4 buildinG blocks
Each T4 template consists of a number of blocks which affect the generated file. The line
Hello World from the first example is a Text block. Text blocks are copied verbatim from the
template file into the generated file. They can contain any kind of text and can contain other blocks.
In addition to Text blocks, three other types of blocks exist: Expression blocks, Statement blocks,
and Class Feature blocks. Each of the other types of block is surrounded by a specific kind of
markup to identify it. Text blocks are the only type of block that have no special markup.
expression blocks
An Expression block is used to pass some computed value to the generated file. Expression blocks
normally appear inside of Text blocks and are denoted by <#= and #> tags. Here is an example of a
template that outputs the date and time that the file was generated.
c#
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".txt" #>
This file was generated: <#=System.DateTime.Now #>
Code snippet Existential.tt
Vb
<#@ template debug="false" hostspecific="false" language="VB" #>
<#@ output extension=".txt" #>
This file was generated: <#=System.DateTime.Now #>
Code snippet Existential.tt
The expression inside the block may be any valid expression in the template language that is
specified in the template directive. Every time it is run the template evaluates the expression and
then calls ToString() on the result. This value is then inserted into the generated file.
statement blocks
A Statement block is used to execute arbitrary statements when the template is run. Code inside a
Statement block might log the execution of the template, create temporary variables, or delete a file
from your computer, so you need to be careful. In fact, the code inside a Statement block can consist
of any valid statement in the template language. Statement blocks are commonly used to implement
flow control within a template, manage temporary variables, and interact with other systems. A
Statement block is denoted by <# and #> tags which are similar to Statement block delimiters but
without the equals sign. The following example produces a file with all 99 verses of a popular
drinking song.
T4 Building Blocks .
269
c#
< #@ template debug="false" hostspecific="false" language="C#" # >
< #@ output extension=".txt" # >
< # for( int i = 99; i > = 1; i-- )
{ # >
< #=i # > Bottles of Non-alcoholic Carbonated Beverage on the wall
< #=i # > Bottles of Non-alcoholic Carbonated Beverage
Take one down
And pass it around
< # if( i-1 == 0 ) { # >
There's no Bottles of Non-alcoholic Carbonated Beverage on the wall
< # } else { # >
There's < #=i-1 # > Bottles of Non-alcoholic Carbonated Beverage on the wall
< # } # >
< # } # >
Code snippet DrinkingSong.tt
Vb
< #@ template debug="false" hostspecific="false" language="VB" # >
< #@ output extension=".txt" # >
< # For i As Integer = 99 To 1 Step -1 # >
< #= i # > Bottles of Non-alcoholic Carbonated Beverage on the wall
< #= i # > Bottles of Non-alcoholic Carbonated Beverage
Take one down
And pass it around
< # If i - 1 = 0 Then # >
There's no Bottles of Non-Alcoholic Carbonated Beverage on the wall.
< # Else # >
There's < #= i-1 # > Bottles of Non-alcoholic Carbonated Beverage on the wall.
< # End If # >
< # Next # >
Code snippet DrinkingSong.tt
In the preceding example the Statement block contains another Text block,
which in turn contains a number of Expression blocks. Using these three block
types alone enables you to create some very powerful templates.
Although the Statement block in the example contains other blocks, it doesn’t need to. From within
a Statement block you can write directly to the generated file using the Write() and WriteLine()
methods. Here is the example again using this method.
c#
< #@ template debug=”false” hostspecific=”false” language=”C#” #>
<#@ output extension=”.txt” #>
<#
270 .
chaPter 14 code generATion WiTh T4
for( int i = 99; i > 1; i-- )
{
WriteLine( “{0} Bottles of Non-alcoholic Carbonated Beverage on the wall”, i);
WriteLine( “{0} Bottles of Non-alcoholic Carbonated Beverage”, i );
WriteLine( “Take one down” );
WriteLine( “And pass it around” );
if( i - 1 == 0 ) {
WriteLine(
“There’s no Bottles of Non-alcoholic Carbonated Beverage on the wall.” );
} else {
WriteLine(
“There’s {0} Bottles of Non-alcoholic Carbonated Beverage on the wall.”,i-1);
}
WriteLine( “” );
} #>
Code snippet ImperativeDrinkingSong.tt
Vb
<#@ template debug=”false” hostspecific=”false” language=”VB” #>
<#@ output extension=”.txt” #>
<# For i As Integer = 99 To 1 Step -1
Me.WriteLine(“{0} Bottles of Non-alcoholic Carbonated Beverage on the wall”, i)
Me.WriteLine(“{0} Bottles of Non-alcoholic Carbonated Beverage”, i)
Me.WriteLine(“Take one down”)
Me.WriteLine(“And pass it around”)
If i - 1 = 0 Then
WriteLine(“There’s no Bottles of Non-Alcoholic Carbonated Beverage on the” &_
“ wall.”)
Else
WriteLine(“There’s {0} Bottles of Non-alcoholic Carbonated Beverage on the” &_
“ wall.”,i-1)
End If
Me.WriteLine( “” )
Next #>
Code snippet ImperativeDrinkingSong.tt
The final generated results for these two templates are the same. Depending on the template, you
might find one technique or the other easier to understand. It is recommended that you use one
technique exclusively in each template to avoid confusion.
class feature blocks
The final type of T4 block is the Class Feature block. These blocks contain arbitrary code that can
be called from Statement and Expression blocks to help in the production of the generated file. This
often includes custom formatting code or repetitive tasks. Class Feature blocks are denoted using
<#+ and #> tags which are similar to those that denote Expression blocks except that the equals sign
in the opening tag becomes a plus character. The following template writes the numbers from –5
T4 Building Blocks .
271
to 5 using a typical financial format where every number has two decimal places, is preceded by a
dollar symbol, and negatives are written as positive amounts but are placed in brackets.
c#
<#@ template debug=”false” hostspecific=”false” language=”C#” #>
<#@ output extension=”.txt” #>
Financial Sample Data
<# for( int i = -5; i <= 5; i++ )
{
WriteFinancialNumber(i);
WriteLine( “” );
} #>
End of Sample Data
<#+
void WriteFinancialNumber(decimal amount)
{
if( amount < 0 )
Write(“(${0:#0.00})”, System.Math.Abs(amount) );
else
Write(“${0:#0.00}”, amount);
}
#>
Code snippet FinancialData.tt
Vb
<#@ template debug=”true” hostspecific=”false” language=”VB” #>
<#@ output extension=”.txt” #>
Financial Sample Data
<# For i as Integer = -5 To 5
WriteFinancialNumber(i)
WriteLine( “” )
Next #>
End of Sample Data
<#+
Sub WriteFinancialNumber(amount as Decimal)
If amount < 0 Then
Write(“(${0:#0.00})”, System.Math.Abs(amount) )
Else
Write(“${0:#0.00}”, amount)
End If
End Sub
#>
Code snippet FinancialData.tt
Class Feature blocks can contain Text blocks and Expression blocks but they cannot contain
Statement blocks. In addition to this, no Statement blocks are allowed to appear once the first Class
Feature block is encountered.
272 .
chaPter 14 code generATion WiTh T4
Now that you know the four different types of T4 blocks that can appear within a template file, it’s
time to see how Visual Studio 2010 is able to use them to generate the output file.
how t4 works
The process of generating a file from a T4 template comprises two basic steps. In the first step,
the .tt file is used to generate a standard .NET class. This class inherits from the abstract
(MustInherit) Microsoft.VisualStudio.TextTemplating.TextTransformation class and
overrides a method called TransformText().
In the second step, an instance of this class is created and configured, and the TransformText
method is called. This method returns a string that is used as the contents of the generated file.
Normally, you won’t see the generated class file but you can configure the T4 engine to make a copy
available by turning debugging on for the template. This simply involves setting the debug attribute
of the template directive to true and saving the template file.
After a T4 template is executed in Debug mode a number of files are created in the temporary folder
of the system. One of these files will have a random name and a .cs or a .vb extension (depending
on the template language). This file contains the actual generator class.
You can find the temporary folder of the system by opening a Visual Studio
command prompt and entering the command echo %TEMP%.
This code contains a lot of pre-processor directives that support template debugging but
make the code quite difficult to read. Here are the contents of the code file generated from the
FinancialSample.tt template presented in the previous section reformatted and with these
directives removed.
C#
namespace Microsoft.VisualStudio.TextTemplatingBE7601CBE8A6858147D586FD8FC4C6F9
{
using System;
public class GeneratedTextTransformation :
Microsoft.VisualStudio.TextTemplating.TextTransformation
{
public override string TransformText()
{
try
{
this.Write("\r\nFinancial Sample Data\r\n");
for( int i = -5; i < = 5; i++ )
{
WriteFinancialNumber(i);
WriteLine( "" );
}
this.Write("End of Sample Data\r\n\r\n ");
How T4 Works .
273
}
catch (System.Exception e)
{
System.CodeDom.Compiler.CompilerError error = new~CA