If you look at the work that a normal developer does most of the time, it's a
fairly repetitive job wherein he writes code for doing a particular module which
is similar to everything else that is done with small differences being say, the
fields that need to be displayed/updated to a different table in a different
database. There are many other things that are similar as well.
However, there is a way to not just automate this work so that the developer
has to only fill in the requirements and the code gets generated, but also
ensure that the code is up to standards as defined by policy. This can make the
architect's and project manager's, not to mention the developer's life also much
better. There is a technology in the .NET world that allows you to do this
called T4.
T4 is an abbreviation for Text Template Transformation Toolkit. This is way
of writing a 'template' that can generate code from a set of rules, other code,
or data input. You can write a template that can be used multiple times with a
slight change in input which will output code for different scenarios. Fairly
complex scenarios are possible by using this. Let's start with some simple
examples and then move on to some more complex ones to see how they work.
Direct Hit! |
Applies To: Web Developers |
First of all, fire up Visual Studio 2010 (or Visual Studio 2008 with the free
T4 Toolbox installed). Note that in VS2008, you will not see an item type in
the new item list and will need to create a text file yourself. In VS2010,
simply create an 'Empty Project' and then add a new item of type 'Text
Template'. This will create a new file with the extension of '.TT'. When the
file opens in VS, you will see the following lines:
<#@ template debug="false" hostspecific="false"
language="C#" #>
<#@ output extension=".txt" #>
There are a couple of things to note here. The language attribute in the
first line specifies that language that is going to be used for creating the
template — not the language that is the output. The extension attribute in the
second line specifies the type of file that is going to be generated. Expand the
.TT file you created in Solution Explorer and you'll see a .TXT file with the
same name. Now change the value in the extension to say '.cs' and it will change
the file as well. Change the .TT file to the following:
<#@ template debug="false" hostspecific="false"
language="C#" #>
<#@ output extension=".cs" #>
using System;
namespace Hello
{
class Program
{
public void Main()
{ Console.WriteLine("Hello World"); }}}
As soon as you save the file, open up the associated .CS
file. You should see the entire code (other than the T4 directives at the top)
generated for you. Congrats, you've just auto-generated your first piece of
code.
Text Template item type in VS 2010 |
However, this is not really impressive right now. You've
actually written more than what the code actually is. But think of a case where
you might need to write this exact same code out, but just change the message
that is shown. For this, you now need to start writing T4 code itself to allow
the template to accept parameters and use them in the code generation.
Modify the code above by changing the Writeline line and
adding the following:
{ Console.WriteLine("<#= this.Message
#>");}}}
<#+
string Message = "Hi There";
#>
When you save this file and open the .CS one, you will see
that the message has indeed changed. The '<#=' and the '<#+' signs are some T4
directives to tell the T4 compiler what needs to be done. Now by simply changing
the value in the Message variable, you can output different code files each
time.
An autogenerated ASP.NET WebForm using a T4 template |
You can also include other code files to your own. For
instance, you might want to generate this file after some other processing. So
create a new .TT file, say MyTest1.tt and have the following within it:
<#@ template debug="false"
hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#
this.Message = "Hallo Welt!";
#>
<#@ include file="HelloWorld.tt" #>
Open the MyTest1.cs file, and you'll find a German version
of the 'Hello World' message. You can of course go ahead and generate a bunch of
code more complex than this of course. As an example, let's take a look at some
templates that automatically generate ASP.NET files - both ASPX and ASPX.CS -
for working with a database table. For code generation, the developer simply
needs to change an XML file that defines the table and the fields.
A number of T4 template files are needed in this case:
1.WebFormFields.tt: Generates the frontend ASPX and HTML
code
2.CodeBehindFields.tt: Generates backend database
connectivity code (for this article only displays field name)
3.SaveOutput.tt: Saves the ASPX and ASPX.cs file in the
current directory
4.BuildPagesFromXML.tt: Reads the XML file for fields and
then uses the other T4 files to generate
The code in each (abbreviated) looks like this:
WebFormFields.tt
<#+ void GenerateASPXField(string
FName, string Type, string Choices) { #>
<#+ switch (Type) {
case "Text": #>
<#+ break; case "List": #>
<#+ string<> items = Choices.Split(';'); for (int i = 0; i < items.Length; i++)
{ #>
<#+ } #>
<#+ break; default: break; }} #>
This code defines a T4 function that simply outputs a
textbox and a dropdownlist for fields defined as such in an XML file.
CodeBehindFields.tt
<#+ void GenerateCBFieldSave(string FName, string Type, string Choices) {
switch (Type) {
case "Text": #>
Response.Write("Field <#= FName #>; Value= " + txt<#= FName #>.Text);
<#+ break; case "List": #>
Response.Write("Field <#= FName #>; Value= " + ddl<#= FName #>.SelectedValue);
<#+ break; default: break; } } #>
Another autogenerated Webform using the same T4 template |
This defines a T4 function that simply outputs the
fieldname and associated value.
SaveOutput.tt
<#@ template language="C#" hostspecific="true" #>
<#@ import namespace="System.IO" #>
<#+
void SaveOutput(string outputFileName)
{
string templateDirectory = Path.GetDirectoryName(Host.TemplateFile);
string outputFilePath =
Path.Combine(templateDirectory, outputFileName);
File.WriteAllText(outputFilePath, this.GenerationEnvironment.ToString());
this.GenerationEnvironment.Remove(0,
this.GenerationEnvironment.Length);
}
#>
This defines a function that saves both the ASPX and
ASPX.cs files when called.
BuildPagesFromXML.tt
This code will need to generate the standard ASP.NET header
and footer for both files and also the generated code by including the files
above. The main code for generating the fields looks like this:
foreach (XmlNode node in
doc.SelectNodes("/Fields/Field"))
{
FieldName = node.Attributes<"Name">.InnerText;
Type = node.Attributes<"Type">.InnerText;
switch (Type)
{
case "Text":
GenerateASPXField(FieldName, Type, "");
break;
case "List":
GenerateASPXField(FieldName, Type, node.Attributes<"Choices">.InnerText);
break;
}
}
SaveOutput(WebFormName + ".aspx");
foreach (XmlNode node in doc.SelectNodes("/Fields/Field"))
{
FieldName = node.Attributes<"Name">.InnerText;
Type = node.Attributes<"Type">.InnerText;
switch (Type)
{
case "Text":
GenerateCBFieldSave(FieldName, Type, "");
break;
case "List":
GenerateCBFieldSave(FieldName, Type, node.Attributes<"Choices">.InnerText);
break;
}
}
SaveOutput(WebFormName + ".aspx.cs");
The input XML file looks like this:
?>
As you change the form name and the fields, a new Name.aspx
and Name.aspx.cs files with the correct front and backend code will be generated
in the directory. As the entire set of templates was too large for this article,
I'll be uploading the solution to the PCQ Forums where you can pick it up.
T4 templates are powerful ways of automating and
standardizing code generated in your company's projects. You can use this to
enhance productivity as well as let developers work on better value additions to
the project that doing the same repetitive job everyday.