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.

Like
Report
*This post is locked for comments