As you already know, T4 engine performs two steps when generating output from a template.
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
- 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.
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
<#@ template language="C#" #> Hello World
<#@ template language="VB" #> Hello World
- Check the preprocessed template (PreprocessedTextTemplate.vb or .cs) which should look similar to this:
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
- Modify it to look like this, using full path to the assembly that contains the precompiled template and its namespace.
<#@ template language="C#" #> <#@ output extension="txt" #> <#@ assembly name="C:\...\bin\Debug\CsTemplate.dll" #> <#@ import namespace="CsTemplate" #> <# PreprocessedTextTemplate t = new PreprocessedTextTemplate(); this.Write(t.TransformText()); #>
<#@ 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.
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.
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.
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.
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.
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.
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, 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.
- DSL 2010 Feature Dives- T4 Preprocessing - Part One - Rationale by Gareth Jones
- DSL 2010 Feature Dives- T4 Preprocessing - Part Two - Basic Design by Gareth Jones
- VS10 Beta 1 / T4 Preprocessing part 1 by Pablo Galiano
- VS10 Beta 1 / T4 Preprocessing part 2 by Pablo Galiano
- Why is a Preprocessed Template the Coolest Thing since Sliced Bread by Kathleen Dollard