Understanding T4: Preprocessed Text Templates


As you already know, T4 engine performs two steps when generating output from a template.

Template Transformation Process

During the first step, the engine preprocesses the template: it parses the processing instructions, text and code blocks, generates a concrete TextTransformation class, and compiles it into a .NET assembly. During the second step, T4 engine creates an instance of the GeneratedTextTransformation class, calls its TransformText method and saves the string it returns to the output file.

Visual Studio 2010 also allows you to preprocess the template at design time, when author of the code generator creates the template itself. At run time, when a developer is using template to generate output code, the hosting application simply creates an instance of the precompiled GeneratedTextTransformation and uses it to generate output as usual. Because preprocessed templates have no hard-coded dependency on Visual Studio, any application can host preprocessed templates, as long as it provides appropriate mechanism for saving or presenting the generated output to the user.

At the time of this writing, no official information is available about preprocessed text templates on MSDN. Gareth Jones and Pablo Galiano published useful information on their blogs (see the References section below). This article is meant to fill the gaps and serve as a comprehensive overview and reference of the functionality supported by preprocessed templates.

Creating a Preprocessed Template

  • Create a new C# or Visual Basic project in Visual Studio 2010.
  • Select Project -> Add New Item from the main menu

Adding Preprocessed Text Template to a Visual Basic Project

  • In the Add New Item dialog, select Preprocessed Text Template item, enter appropriate name and click Add button.
  • In Solution Explorer, you should now see the new .tt file and the preprocessed template file it generates. To see the preprocessed template file in a Visual Basic project, make sure to click the Show All Files button in the toolbar of Solution Explorer.

Preprocessed Template in Solution Explorer File Properties of Preprocessed Template

Note that Custom Tool this .tt file was associated with is TextTemplatingFilePreprocessor  instead of the traditional TextTemplatingFileGenerator. Instead of the output file, which would be generated by the traditional template, for the preprocessed template, we the actual source code of the template itself added to the project. This preprocessed template class is compiled as part of the project that contains the .tt file.

  • Modify the .tt file to look like so
C#
<#@ template language="C#" #>
Hello World

Visual Basic
<#@ template language="VB" #>
Hello World

  • Check the preprocessed template (PreprocessedTextTemplate.vb or .cs) which should look similar to this:
C#

Preprocessed Template, C#

Visual Basic

Preprocessed Template, Visual Basic

As you can see, the text, statement, expression and class feature blocks defined in the .tt file become a part of the preprocessed template class just like in a traditional template. However, there are several interesting points to note.

First of all, notice that name of the generated template class is based on the name of the .tt file. In our example, PreprocessedTextTemplate.tt produced class name PreprocessedTextTemplate. In a traditional template, this name would be hard-coded GeneratedTextTransformation. Having file name determine the class name allows us to give template classes meaningful names and organize them in class libraries.

The namespace in which the class is generated is based on the Custom Tool Namespace property of the .tt file. In Visual Basic, this property is My.Templates by default which is what you see generated in the code above. In C# this property is empty by default, causing TextTemplatingFilePreprocessor to construct the namespace based on the default project namespace and location of the .tt file in it. In Visual Basic, clearing the Custom Tool Namespace results in template class being generated in the root namespace of the project. This is also different from traditional templates, which use a hardcoded namespace with a randomly generated suffix, and also allows us to better organize the precompiled templates as part of class libraries.

And last, the preprocessed template class is generated as partial. You can further extend it by adding another partial .cs or .vb file to the project. This allows you to place methods and properties that don’t require templating in a regular source file instead of class feature blocks you would otherwise have to use in a traditional template. Out of the box, Visual Studio offers better debugging experience, IntelliSense and color syntax highlighting for regular source files. However, these differences are reduced only to debugging if you are using the T4 Editor to create your templates.

Using a Preprocessed Template

Executing a preprocessed template is a simple matter of creating an instance of the preprocessed template class and calling its TransformText method. As Pablo Galiano describes here, this can be done in any application. Here we will review how preprocessed templates can be used for code generation in a traditional T4 template, hosted and executed by Visual Studio.

  • Add a traditional text template to the project

Adding Traditional Text Template

  • Modify it to look like this, using full path to the assembly that contains the precompiled template and its namespace.
C#

<#@ template language="C#" #>
<#@ output extension="txt" #>
<#@ assembly name="C:\...\bin\Debug\CsTemplate.dll" #>
<#@ import namespace="CsTemplate" #>
<#
    PreprocessedTextTemplate t = new PreprocessedTextTemplate();
    this.Write(t.TransformText());
#>

Visual Basic
<#@ template language="VB" #>
<#@ output extension="txt" #>
<#@ assembly name="C:\...\bin\Debug\VbTemplate.dll" #>
<#@ import namespace="VbTemplate.My.Templates" #>
<#
    Dim t As PreprocessedTextTemplate = New PreprocessedTextTemplate()
    Me.Write(t.TransformText())
#>

As you can see, in this traditional template, we are creating a new instance of the precompiled template and writing the code it generates to the standard output file, TextTemplate.txt in this case.

  • Check the output file, which should look like this.
Hello World

For simplicity of this example, we have both the preprocessed template and the traditional template using it in the same project. In a real-world scenario, you will typically have a preprocessed template in a separate assembly, which you compile and distribute to your development team for use in other projects.

Differences Between Traditional and Preprocessed Templates

There are several important differences between traditional and preprocessed templates.

Dependency on Microsoft.VisualStudio.TextTemplating assembly

Traditional T4 templates have a dependency on Microsoft.VisualStudio.TextTemplating assembly, which is installed as part of Visual Studio and cannot be redistributed. Preprocessed templates don’t hard-code this dependency and can be used to generate code from a custom hosting application. In the example above, you can see that all code required to execute the preprocessed template is generated as part of its definition and doesn’t rely on any base classes. However, when preprocessed templates are intended for use only within Visual Studio, we can avoid having multiple redundant implementations of the same properties and methods in each template class by using Inherits parameter of the Template directive to specify the standard TextTransformation as the base class.

Template Directive

Language parameter is partially supported. The precompiled template is generated in the specified language, however if template language doesn’t match the language of the project it belongs to, the generated template file will not be compiled. If this happens, the Build Action for the output file is automatically changed to Content.

Debug parameter appears to have no effect on preprocessed templates. Template author is responsible for setting debugging options for the project which contains the preprocessed .tt file.

Inherits parameter is supported and significantly changes the way preprocessed templates are generated. When this parameter is not present, the preprocessed template class will be generated without base class and will have its own implementations of all standard methods inherited by traditional templates from the TextTransformation class. When this parameter is present, the preprocessed template class will be generated with the specified base class and without the standard methods. Template author is responsible for specifying a base class that either inherits from TextTransformation, or provides compatible properties and methods.

Hostspecific parameter is partially supported. The preprocessed template class is generated with the appropriate Host property, however, this introduces compile-time dependency on Microsoft.VisualStudio.TextTemplating assembly. Template author is responsible for adding a reference to this assembly to the project that contains the preprocessed template class. The hosting application is responsible for providing a value for this property at run time.

Culture parameter appears to be fully supported.

Output Directive

The <#@ output #> directive appears to have no effect on preprocessed templates. No error is generated when a preprocessed template contains this directive. The hosting application is responsible for changing the extension of the output file.

Assembly Directive

The <#@ assembly #> directive appears to have no effect on preprocessed templates. No errors is generated when a preprocessed template contains this directive. Template author is responsible for adding appropriate assembly references to the project that contains the .tt file.

Import Directive

The <#@ import #> directive appears to be fully supported by the preprocessed templates. Appropriate using (C#) and imports (Visual Basic) statements are added to the generated template class.

Include Directive

The <#@ include #> directive appears to be fully supported by the preprocessed templates. Template blocks of the included files are merged appropriately in the generated template class.

Custom Directives

Custom directives, such as <#@ xsd #> and <#@ property #> appear to be fully supported by the preprocessed templates. The custom directive processors are invoked appropriately and any additional code they produce is merged with the template code in the generated template class.

References

Download


Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

The use of a partial class in the generated code is a very cool aspect of these templates. It is very easy to extend the code generator and work around some issues I found while experimenting with generating code from DSL models.

[…] Understanding T4: Preprocessed Text Templates (Oleg Sych) – Link of the Day […]

[…] Understanding T4: Preprocessed Text Templates - Oleg Sych digs into some of the details of how T4 Templates work, and looks at the pre-processed text template functionality […]

Great point, Filipe. I’ll update the article to mention partial class declaration.

[…] Understanding T4: Preprocessed Text Templates […]

The change looks fine, I suppose, but I do not see the value. The improvement s allow one to start generating partial classes? Huh? My generator already produces partial classes now. If I want to extend the code without touching the generated code, I simply add another partial class and put my hand-written code in it. Am I missing something here? Also, wrt to namespaces, my code generation template sets already have a parameter that allows my generator output to be in whatever namespace I specifiy. Again, I think I am missing something here? Is it some other place the improvements are addressing? If possible, can somebody, please simplify the value-add a bit, for a newbie? Thank you. — Mark Kamoski

Mark,

Check out the References section above to see what Gareth, Pablo and Kathleen had to say on why preprocessed templates are valuable. I don’t think I can add much to what they have already said.

From my prospective, preprocessed templates are important because they allow authors to develop T4 code generators as traditional .NET assemblies as opposed to sets of .tt files. This can improve performance, development experience and protect intellectual property.

Oleg

[…] Understanding T4 - Preprocessed Text Templates: Oleg Sych explains T4 templates. […]

[…] Oleg Sych very nice blog post on T4 Preprocessed Text Templates […]

Hi Oleg,
New preprocessed templates seem to be going in the right direction imho. I have a question as to what you said here: “However, these differences are reduced only to debugging if you are using the T4 Editor to create your templates.”
Does what you said above mean that you are able to use visual studio’s built-in debugger? This is definately an improvement, if so. TIA.

Brian,

You can use built-in Visual Studio debugger with both pre-processed and traditional templates (more here). When I wrote this article, I didn’t realize that 2010 provides better support for debugging of .tt than it did in 2008, which didn’t handle breakpoints correctly. In 2010, debugging experience for pre-processed and traditional templates is almost the same; in both cases you simply need to have a second instance of Visual Studio to serve as a debugger.

Oleg

[…] new feature in VS2010 can help make T4 templates more TFS friendly. T4 Preprocessed Templates allow T4 code to be translated into C# code, and gives the developer the control over when code […]

[…] reading How to create a Visual Studio 2010 add-in FileCodeModel interface Using T4 Preprocessed templates for code generation WCF […]

Just for clarification, the project type that should be created, for C#, is a “Class Library”. The directions do not specify, and will not work if you create a “Console” app. (I couldn’t get it to work.)