T4 templates

Text Template Transformation Toolkit, or T4, is a .NET technology for generating various text files, including source code files. It allows you to mix plain text and processing instructions (in a similar way like PHP, among others) and these processing instructions can contain usual C# or VB code. One example of its usage is the generating of entity classes from a database schema in ADO.NET Entity Framework.

A very simple template can look like this:

This file was generated at <#= DateTime.Now #>.

You can see some plain text here plus a usage of .NET class DateTime and its property Now, enclosed by some special brackets.

Because this blog is primarily about Dynamics AX, I’ll show one a bit more complex example in AX. It generates a new test class based on a definition of some other class existing in AOT. (The example uses Dynamics AX 2012 and Visual Studio 2010.)

At first, you have to create a Visual Studio project (class library), add it to AOT and create few AX proxy classes: SysDictClass, SysDictMethod, Set and SetEnumerator. Then add a new item of type Preprocessed Text Template. That’s one type of T4 templates.

Insert the following code to the template:

<#@ template language="C#" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Microsoft.Dynamics.AX.ManagedInterop" #>
Exportfile for AOT version 1.0 or later
Formatversion: 1
 
***Element: CLS
 
; Microsoft Dynamics AX Class: <#= ClassName #>Test unloaded
; --------------------------------------------------------------------------------
  CLSVERSION 1
 
  CLASS #<#= ClassName #>Test
    PROPERTIES
      Name                #<#= ClassName #>Test
    ENDPROPERTIES
 
    METHODS
      SOURCE #classDeclaration
        #class <#= ClassName #>Test extends SysTestCase
        #{
        #}
      ENDSOURCE
<# foreach (SysDictMethod method in Methods){
#>      SOURCE #test<#= Capitalize(method.name()) #>
        #public void test<#= Capitalize(method.name()) #>()
        #{
        #    //TODO: implement unit test
        #}
      ENDSOURCE<#}#>
    ENDMETHODS
  ENDCLASS
 
***Element: END
<#+ public string ClassName {get; set;}
private IEnumerable<SysDictMethod> Methods
{
    get
    {
        SysDictClass dictClass = SysDictClass.newName(ClassName);
        SetEnumerator enumerator = dictClass.methods(true, true, false).getEnumerator();
        while (enumerator.moveNext())
        {
            yield return new SysDictMethod((Microsoft.Dynamics.AX.ManagedInterop.Object)enumerator.current());
        }
    }
}
private string Capitalize(string s)
{
    return s.Substring(0, 1).ToUpper() + s.Substring(1);
}#>

As you can see, the template generates a usual .xpo file. Let’s take a closer look at some parts of it.

It begins with the template header and the declaration of some namespaces which we want to use in the template.

<#@ template language="C#" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="Microsoft.Dynamics.AX.ManagedInterop" #>

Then there is some plain text with occasional constructs <#= ClassName #>. <#= means that the output of the statement will be added as a text to the generated file. And ClassName is a name of a property defined later in the template.

The next interesting section is this:

<# foreach (SysDictMethod method in Methods){
#>

You can easily recognize a usual C# code inside (Methods is another property defined later). Notice that this part is introduced by <#, not <#=. It means that the context should be just run, without anything returned to the output. Here we iterate through all method in a collection and repeat the whole section (between foreach and the closing brace in <#}#> statement) for each method.

In the foreach body we use the already known construct returning a value, this time calling some methods:

 <#= Capitalize(method.name()) #>

At the end of the template, we have another special part introduced by <#+. This section is used for declaring class members – properties ClassName and Methods and Capitalize() method, in our case.

Summary: The template expects to get the name of an AX class and it generates a test class and one test method for each (non-inherited) method in the source class. (It would be more complex in a real implementation, but it’s good enough for the demonstration.)

Now let’s use it inside Dynamics AX. Build and deploy the project with T4 template, go to AX and create a job with a similar code:

str className = 'Stack';
str xpoText;
TestClassGenerator.TestClassTemplate template;
SysImportElements sysImportElements = new SysImportElements();
FileName fileName;
TextIo io;
 
//Create instance of T4 template (name depends on how you named project and template in VS)
template = new TestClassGenerator.TestClassTemplate();
//Use template's property to set class name
template.set_ClassName(className);
//Let template generate .xpo file content
xpoText = template.TransformText();
 
//Write .xpo to a temporary file
fileName = WinAPI::getTempFilename(WinApi::getTempPath(), '');
io = new TextIo(fileName, 'w');
io.write(xpoText);
 
//Import .xpo
sysImportElements = new SysImportElements();
sysImportElements.newFile(fileName);
sysImportElements.parmAddToProject(false);
sysImportElements.parmImportAot(true);
sysImportElements.import();
 
//Open newly created class
TreeNode::findNode(strFmt(@'Classes\%1Test', className)).AOTnewWindow();

When you run the job, the following class should be generated:

As you can see, T4 templates can be really useful. Especially if the target file contain a large amount of static text, T4 is an excellent choice. You may not use it every day, but it’s good to know about it.