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

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.

[…] 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

ITextTemplatingEngineHost.ResolvePath isn’t described at MSDN and it doesn’t work for me. I need it very much! Could you fix it?

Alexey,

Could you provide specific details about the problem you are experiencing with ResolvePath?

Oleg

This might be out of the realm of T4 Template processing, but I have a really simple issue that I can’t solve.
I have a Base Template that contains Database connection information. Then I have a child of that Base template that does all the work generating the file. I then have a Child of the Child that sets specific parameters that will be used in the middle tier template to generate the text. If I separate them into a nice folder structure using relative paths in the include directive’s it throws and error saying: “Failed to resolve include text for file:{0} ” on the Base template. If I throw them all in the same folder and update the include directives it then works fine. I know the relative paths for the folder structures check out since its simple like: Folder1 contains BaseTemplate.tt, Folder2 a sub folder of Folder1 contains ChildBase.tt, Folder3 a sub folder of Folder2 contains Child.tt.
Child.tt include:
ChildBase.tt include:

Justing, please take a look at the Visual Studio T4 Host section at http://www.olegsych.com/2008/02/t4-include-directive/

Thanks Oleg.

[…] Oleg Sychi postitusi T4 mallide kohta. Olen ka neile varem viidanud. Nüüd viitaks konkreetselt T4 Template direktiivile. Seal see lahendus […]

[…] If you want to use the C# 3.5 compiler and LINQ features, make sure to use the correct template and assembly directives. Oleg Sych has the details: Understanding T4: <#@ template #> directive. […]

Hi Oleg,

I need to convert an xsd (XML schema) file to class file. I understand that .cs file for ‘GeneratedTextTransformation’ is generated at the time of compilation. This will contain the class representation of the xsd that I have specified in the ‘xsd’ tag. Can I have that content of the class file outputed as the output of the T4 template.
Thank you very much in advance.

Abdul,

You can either change your template to be pre-processed or use the xsd.exe command-line tool.

oleg

Hi,

I have written a class which inherits from TextTransformation. Is there anyway to get a T4 Template to use this as it’s base class

(I have tried using the inherits parameter, but it does not seem to work unless I use a “pre-processed template” - which is not what I want, as I want to actually generate an output, and not a class that I then run)

Do you have any idea how I go about doing this?

How have you guys been able to inherit from TextTransformation? I’m pulling my hair out trying to find this class.

I have added this project reference:
Microsoft.VisualStudio.TextTemplating.Modeling.10.0.dll

And I have this using statement:
using Microsoft.VisualStudio.TextTemplating;

And still the class is not found. Using Object Browser on that assembly shows TextTransformation is not in there (at least, it’s not public).

Note that I’m runing .NET 4.0.30319 RTMRel and VS 2010 10.0.30319.1 RTMRel.

What am I missing guys?

Thanks!

Harvo

Oy, I’m such an idiot.

Burried under GAC_MSIL I found Microsoft.VisualStudio.TextTemplating.10.0.dll (without “Modeling”). For some reason this didn’t show up in my .NET references, nor was in present under the IDE’s PublicAssemblies directory.

Why? I don’t know, but I’m all good now!

- Harvo

[…] Previously, if you had a runtime template that declared itself as host-specific, […]

[…] Previously, if you had a runtime template that declared itself as host-specific, […]

[…] Previously, if you had a runtime template that declared itself as host-specific, […]

On July 31 Oleg said, “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.”

Boy, VS team has done everyone a disservice by training us to think like that. The whole invention of ’source control’ is to prevent that very thing. The TT file is the source, not the CS file that comes from it.

The template tool is equivalent to the old compiler that generated OBJ files. No one in their right mind would check in OBJ files if they had a professional and well-designed build and development tools set.

As a matter of fact, Microsoft ran “clean” builds of the entire windows operating system even before 1995. The never checked in OBJs, and to this day they still don’t. Why? Because the build tools are predictable, and the real source code is in source control.

Visual Studio simply blew it by ignoring their own in-house experts on software development, putting together this system that is some inferior model from the 80s.

@bob, I believe the analogy between .OBJ files and generated .CS holds true only if model that was used to generate code is in source control. With .OBJ files, the model is the original .C file. With T4, the model can be external to the solution; for instance, it is very common to use .TT files generate code from database schema. For practical reasons, storing an entire database under source control is feasible only in trivial cases. In a fast-paced development environment, being able to see history of changes in the generated code that could be a result of both model and the code generator changing is invaluable.

Consider the alternative used by frameworks like ASP.NET where markup files serve as source code and the actual code is generated at run time, invisible, unless you know where to look. While this approach results in a dramatic decrease in the visible source code of the solution, it is also the most difficult to understand. As much as people loved WinForms they seemed to hate WebForms for this complexity, which is why, perhaps, Microsoft created the MVC and Web Page frameworks to address the problem.

[…] initialization lists or even inheritance failed. And then I found the answer to the problem on Oleg Sych’s great blog. In VS2008 you can specify which version of the language you want to use. For instance v3.5 for […]