Understanding T4: <#@ template #> directive


Here is how MSDN documentation defines the template directive.

By using the template directive, you can specify characteristics of the generated transformation class. For example, you can specify code language, class inheritance, culture, and the ability to debug.

This directive contains several parameters. Most of them are well described in MSDN documentation. From code generation prospective, several parameters deserve more attention.

Language parameter

As the documentation explains, language parameter specifies which language, either Visual Basic or C# is used in code blocks of the template. Acceptable values include "C#" and "VB". By default, T4 will use version 2 of the C# and Visual Basic compilers. In Visual Studio 2008, you can take advantage of the new language features provided by version 3.5 of the compilers by including version in the parameter value [1]. 

<#@ template language="C#v3.5" #>

or

<#@ template language="VBv3.5" #>

New assemblies shipped with version 3.5 of the .NET framework need to be referenced explicitly:

<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>

Debug parameter

As the documentation explains, setting the debug parameter allows you to debug templates. In order to make it possible, T4 saves generated source code, .NET assembly and debugging symbols in the TEMP folder (%USERPROFILE%\Local Settings\Temp). The .cs file contains source code of the GeneratedTextTransformation. The .cmdline file contains command line options used to compile .cs file into .NET assembly (the .dll file).

Template

<#@ template language="C#" debug="True" #> 

Generated Temporary Files

csqg2m5s.0.cs
csqg2m5s.cmdline
csqg2m5s.dll
csqg2m5s.err
csqg2m5s.out
csqg2m5s.pdb
csqg2m5s.tmp

Note that setting the language parameter to "VB" causes T4 to generate Visual Basic code and use vbc instead of csc to compile the GeneratedTextTransformation.

Inherits parameter

Here is how MSDN documentation defines the inherits parameter of the template directive.

Specifies which class should be used as the base class for the generated transformation class. The following example shows how to specify that the SomeClass class should be used:

<#@ template inherits="SomeClass"#>

In order to demonstrate use of this parameter, we need to have a class that inherits from TextTransformation. Here is an example of such class.

MyTextTransformation.cs

using System;
using Microsoft.VisualStudio.TextTemplating;

namespace ClassLibrary1
{
    public abstract class MyTextTransformation: TextTransformation
    {
        protected void SayHello()
        {
            Write("Hello");
        }
    }
}

With previous code compiled in ClassLibrary1.dll, we can see how T4 uses it under the hood.

Template

<#@ template inherits="MyTextTransformation" debug="True" #>
<#@ assembly name="C:\ClassLibrary1\bin\Debug\ClassLibrary1.dll" #>
<#@ import namespace="ClassLibrary1" #>
<#
    SayHello();
#>

Compiled Template

using System;
using Microsoft.VisualStudio.TextTemplating.VSHost;
using ClassLibrary1;

namespace Microsoft.VisualStudio.TextTemplating813D4B84ADE998BE3BDDB17BECBBF62A
{
    public class GeneratedTextTransformation: MyTextTransformation
    {
        public override string TransformText()
        {
            SayHello();
            return this.GenerationEnvironment.ToString();
        }
    }
}

Output

Hello

As you can see, the template above uses the inherits attribute of the template directive to change base class of GeneratedTextTransformation to MyTextTransformation. Having replaced the base class, template can access its methods, such as SayHello. It is important to note that for this template to compile, T4 needs be able to find the base class. In this example, we are using the assembly directive to let T4 know the assembly where this class is defined and the import directive to specify it’s namespace. Using the assembly directive would not be necessary if the base class was defined in an assembly stored in GAC. Using the import directive would not be necessary if we specified full name of the class in the inherits attribute

Hostspecific parameter

Here is how MSDN documentation defines the hostspecific parameter of the template directive

Used only with custom hosts. If you set the value of this parameter to true, you can access a property called Host in your text template. The property is a reference to the object that hosts the engine. You should set hostspecific to true only if you want to write a text template that is specific to a custom host and the text template calls the host at execution time. When hostspecific is true and you are using Visual Studio, you can call GetService to update your text templates.

Here is what happens under the hood.

Template

<#@ template language="C#" hostspecific="True" #>
Generated by <#= Host.TemplateFile #>

Compiled Template

using System;
using Microsoft.VisualStudio.TextTemplating;  

namespace Microsoft.VisualStudio.TextTemplatingEA2810A00CCCB9610
{
    public class GeneratedTextTransformation: TextTransformation
    {
        public override string TransformText()
        {
            this.Write("Generated by ");
            this.Write(ToStringHelper.ToStringWithCulture(
                Host.TemplateFile));
            return this.GenerationEnvironment.ToString();
        }  

        private ITextTemplatingEngineHost hostValue;
        public virtual ITextTemplatingEngineHost Host
        {
            get { return this.hostValue; }
            set { this.hostValue = value; }
        }
    }
}

Output

Generated by C:\...\HostSpecificParameter.tt

As you can see, this template sets the hostspecific parameter to True, which made T4 add Host property to the GeneratedTextTransformation. This property returns ITextTemplatingEngineHost interface, which provides access to several important properties and methods, such as ResolvePath which can be used to resolve file paths relative to the location of the template file into absolute paths.

About T4

T4 (Text Template Transformation Toolkit) is a template-based code generation engine. It is available in Visual Studio 2008 and as a download in DSL and GAT toolkits for Visual Studio 2005. T4 engine allows you to use ASP.NET-like template syntax to generate C#, T-SQL, XML or any other text files.

Ready for more? Read about <#@ output #> directive or go back to the overview.

References

  1. Enabling C# or VB 3.5 inside a text template by Pablo Galiano

Information and Links

Join the fray by commenting, tracking what others have to say, or linking to it from your blog.


Other Posts
Understanding T4: <#@ output #> directive
Understanding T4: Class Feature Blocks

Write a Comment

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

Reader Comments

Hi, Oleg!
But what about the “inherits” attribute?
I can’t force a T4 template to inherit from my class, an inheritor of TextTransformation.

Great question, Alex. I updated this post to include an example of using the inherits parameter. I also updated the post about assembly directive to show how to reference assemblies that are not stored in GAC. I hope it’s helpful.

Yep, thanks for the answer, Oleg.
I have already completed the quest, but faced a much more complicated problem. I have two projects in the same solution, one for the base class, and one for the templates consuming it. But since the templates transformed once by VS, the output .dll in the first project is locked by VS, and I can’t rebuild the assembly with the inherited class.

Is there any way to avoid the locking?

Yes, I really want to get CodeSmith’s Code Behind functionality just for free, and an ability to edit the code-behind file using Visual Studio and cover it with unit tests :)

Alex,

If I understand correctly, Visual Studio T4 host reuses a single AppDomain for multiple template transformations to improve performance.

One possible solution may be to use a different host, such as TextTransform.exe (command line T4 host), which doesn’t cache assemblies.

It may also be possible to force Visual Studio host to unload the templating AppDomain by using reflection to call UnloadGenerationAppDomain method of the object returned by template’s Host property. It’s an internal method defined in Visual Studio T4 host class (TextTemplatingService).

Let me know if you get this working. If not, I’m afraid we may be limited to using < #@include #> directive.

Oleg

Thanks for the idea, Oleg.
I decided to implement my own ITextTemplatingEngineHost in order to be able to apply my own directive processors. I need it to provide template parameters to the text transformer. It could allow using the same template with multiple parameter sets, exactly as we use CodeSmith templates.

Implementation of the host was not a problem at all, I’d just copied the sample code from MSDN’s how-to. But I can not attach my own directive processor, and even can not understand what’s going on within the engine, as my breakpoints are ignored. But it seems that the engine throws an exception when I attempt to attach a directive processor that is not referenced to in the windows registry.

Update:
It was my fault. I didn’t add the attribute pointing to the DirectiveProcessor into the directive tag. Now fighting the reference hell.

Final results.

Yesterday I have created my custom template host and custom directive to provide parameters to the template renderer. The VS integrated template processing tool is no good as it doesn’t contain any way so provide parameters to the renderer. It means that I am to create a file format to store template parameter sets (equivalent of CodeSmith’s projects), certain VS project type and MSbuild task to build output scripts for all the setting sets in one template renderer run. It guarantees that the database schema and other metadata will be loaded only once. That’s what CodeSmith can’t do. Visual Studio also guarantees that the output .dll of the projects containing the basic class we inherit will be placed in the project’s output directory. So we can even build a multiple-time usable .exe that will consume parameter file names as the command line args. It seems to be operational… in theory :)

Alex,

That’s quite impressive. I would love to see your code and understand better what you are trying to accomplish. I also kept an idea of building a T4 MSBuild task in the back of my mind. Perhaps we could work on this together. Do you have a blog?

About storing template parameter sets… Even though T4 is not as advanced as CodeSmith project file, there are ways to achieve the same results without having to build and deploy any additional tools. For example, in How to generate multiple outputs from single T4 template I described two methods of passing parameters to templates. One is using helper method parameters and the other is using CallContext. With either method, the calling template works as an equivalent of a CodeSmith project file. I would be interested in your thoughts on this.

Hi, Oleg!

Sure, I am glad to share the code, though it is dirty yet. In my implementation the only option is to provide the template with the path to the file where it can take settings from.
I have just started my blog at http://blog.alexshestakov.com and in the post coming soon I am going to describe the background of my implementation of the entity engine.

Concerning the transformation host in the VS: I am not sure I like to have my templates rendered every time I save the template source. Actually, rendering of some database schema templates could take very much time. And, what is even more important, I would like to create stand-alone code generators (without an ability to edit a template, but for multiple parameter sets), as in the real world the code generation is usually run because of changes in a parameter set, rather than in a template itself.

I can email you the sources, you may send a request to my email at any time.

Added description of the language parameter and a link to Pablo Galiano’s article - Enabling C# or VB 3.5 inside a text template

[…] Checking out Oleg Sych’s blog for great T4 articles including how to enable the 3.5 compiler for templates […]

Good series on T4 from your blog Oleg - thanks.

I’ve put together a simple MSBuild task which executes a T4 template. It lets you reference build properties and item groups in the template and will replace them with the runtime values from MSBuild:

http://geekswithblogs.net/EltonStoneman/archive/2008/07/25/an-msbuild-task-to-execute-t4-templates.aspx

Elton.

How do you cause a T4 template to generate its output file upon the VS 2008 solution (.sln) file being built?

There are only two ways of running the template file that I’ve found:
– Right-clicking on the .tt file, and selecting “Run Custom Tool”
– Editing the content of the .tt file seems to automatically run it

I am using ClearCase, and it would seem best to add to source control only the .tt files, not the generated .cs files.

Thanks,
-Mike

Mike,

To transform T4 templates during build you could use Elton Stoneman’s MSBuild task for transforming T4 templates and customize MSBuild scripts to compile the output files they generate. However, unless you remove TextTemplatingFileGenerator custom tool from .tt file properties, Visual Studio will continue transforming the templates every time you save them.

I’m not sure this approach is better than code generation at design time only, which is what T4 and other code generation tools in Visual Studio are designed to do. I would be particularly concerned about not having the generated source code stored in source control repository. Because development environment can change, there is no guarantee that you can regenerate the exact same code that was shipped or delivered to production months or years ago. Troubleshooting and debugging in this situation becomes very challenging.

Oleg

Thanks for the sound advice, Oleg.

Originally I checked in the generated .cs files so as to not break the build, but given your perspectives, it sounds like it is also the final solution.
-Mike

For what it’s worth, I discovered the answer to my original question – how to get VS 2008 to run a template when building (although now I don’t plan to use it as my final solution, per Oleg’s advice).

Here’s how to do it:

1. View the properties of the project file (.csproj).

2. Add a pre-build step as follows (for a template called foo.tt):
“C:\Program Files\Common Files\Microsoft Shared\TextTemplating\1.2\TextTransform” -out $(ProjectDir)\foo.cs -I $(ProjectDir) $(ProjectDir)\foo.tt

3. Save and build.

Good luck,
-Mike