T4 Templates: A Quick-Start Guide for ASP.NET MVC Developers
As mentioned in our recent blog post on the ASP.NET MVC Release Candidate, our code-generation features (namely, Add Controller and Add View) now use the T4 (Text Template Transformation Toolkit) templating technology behind the scenes. Because users can customize the templates to a great extent, we wanted to make a post to bring everyone up to speed on T4.
Template Locations and Template Override
The Add Controller and Add View dialogs both perform code generation that use T4 templates behind the scenes. These templates can be modified to customize the generated code from these tools. You can find the templates at the following location: [Visual Studio Install Directory]\Common7\IDE\ItemTemplates\[CSharp | VisualBasic]\Web\MVC\CodeTemplates\
You can also copy the ‘CodeTemplates’ folder into the root of your project to be able to override the templates at the above location and customize the templates on a per-project basis (alternatively, just create a folder named ‘CodeTemplates’ and under that create a folder named ‘AddController’ or ‘AddView’). Note that you can choose to override some templates but not others if you so wish – the dialogs will respect the precedence of what is in your project properly. Also keep in mind that for the Add View dialog, you can add your own .tt files either at the global location or in your project and have them automatically populated in the View Content drop-down in the dialog.
Please note that when you copy the above folder (really, any time you add a .tt file) into the project, you will see warnings as follows:
Hit ‘Cancel’ so that you don’t run the T4 template (if you are adding multiple .tt files like when you copy the ‘CodeTemplates’ folder, you will have to hit ‘Cancel’ each time). This happens because as soon as the project sees a .tt file, a property on the file called ‘CustomTool’ will get set to ‘TextTemplatingFileGenerator’ What this is telling Visual Studio to do is to use the default T4 host to execute the template and create a new file (nested underneath the template) based on what’s in the template.
The generator can be a great way to have one-off file generation based on a template, or to simply play around with T4 – however, because Add View and Add Controller templates have code in them that rely on a custom template host (as you’ll see later), executing these templates with the default generator will simply generate errors. Go ahead and clear out the Custom Tool property to just be empty after copying the templates into your project.
Note: If you want to get rid of the mapping that automatically sets the Custom Tool on .tt files, you can do so through the registry – please note that if you want to restore these registry keys to their default values, you can perform a Repair from the Visual Studio installer, or add the entries back manually. Start up the registry editor (Start –> Run –> ‘regedit’) and navigate to one of the following locations depending on whether you are using a 32-bit or 64-bit install of Windows:
Expand each of the nodes directly under ‘Generators’ and look for any entries named ‘.tt’. Set the registry value named ‘(Default)’ to be empty. That’s it!
If you want to override the global templates but don’t want to copy a folder named ‘CodeTemplates’ into your project (since you already have a folder by that name presumably), you can change the name of the folder Add Controller and Add View look at through a registry key. Start up the registry editor (Start –> Run –> ‘regedit’) and navigate to one of the following locations depending on whether you are using a 32-bit or 64-bit install of Windows:
Change the value for ‘OverrideDir’ to what you want the tooling to look for in your project. Please note that under this folder you will still have to maintain the same hierarchy as before, with a folder named ‘AddController’ and/or a folder named ‘AddView’.
Editing T4 Templates
If you open a .tt file inside Visual Studio, you may notice they look a bit flat – like a wall of black text. We highly highly recommend you download T4 Editor, an add-on to Visual Studio made by Clarius Consulting, to provide you with syntax highlighting and some basic T4 statement completion. They have a free version called the ‘Community Edition’, as well as a much more powerful ‘Professional Edition’ – check out the feature comparison if you are interested. This add-on really makes a big difference in template authoring, as you can see below.
Anatomy of a T4 Template
The easiest way to get started with editing the templates yourself is to see how our default templates work. To that end, we’re going to go through some of the pieces of the ‘Create’ template (Create.tt) for Add View. Let’s start at the top:
These four lines are all directives.
- The first line is the template directive, and its main job is to tell the T4 engine what language the template itself uses. When we say the template’s language, we don’t mean the language in which the output is written, but rather the template’s control code (for example, your template might include an if-else statement to conditionally output some line of text). The ‘HostSpecific’ attribute has to be set to ‘True’ to work with Add View or Add Controller, because otherwise the template cannot access the information the Add Controller and Add View dialogs want to provide them (things like the Type to which a strongly-typed view is bound etc)
- The next line is the output directive, and it simply sets the default extension for the template’s output file by informing the template host – this isn’t particularly relevant to MVC Tools since depending on the situation the default extension may be ignored (for example, if we are dealing with a partial view, where the extension should be .ascx)
- The next couple lines are both import directives, and they are like ‘using’ statements in C# or ‘Imports’ statements in VB. If your template’s code uses any classes in its code, you will need to import them here.
You can find more documentation on T4 directives here: http://msdn.microsoft.com/en-us/library/bb126421.aspx
Moving down just a bit, you will notice the following line:
On this line we are creating a new variable named ‘mvcHost’, and assigning it to a casted version of a property named ‘Host’. The ‘Host’ property is something provided to the template automatically because we set the ‘HostSpecific’ attribute to ‘True’ in the template directive above. The MVC Tools provide a custom host, so as to be able to pass information to the templates that are not available outside of the tools. To access the properties provided on our host class, the ‘Host’ property needs to be casted to our host type, which is ‘MvcTextTemplateHost’.
How is this variable used? It is actually quite straightforward:
The first thing to notice is that there is code enclosed within ‘<#’ and ‘#>’ tags. These tags are called statement blocks and are used to enclose control code. Your template may want to conditionally output certain chunks of text into the output file and keep other chunks from being outputted. Above, we have an if statement (written in C# since our template’s language was set to C# in the template directive) that opens a curly brace. This if statement’s closing curly brace shows up a few lines below on Line 17, in a different statement block. Note that this particular if statement is accessing a property on the host called ‘IsViewUserControl’, which tells the template if the user chose to go with a partial view.
All the text outside of blocks in a template are actual chunks of text that are outputted to the final file. In the screenshot above, Line 14’s text is outside of any blocks and is thus part of the output – however, it will be placed into the output file only if the if statement on Line 12 evaluates to ‘true’.
You can learn more about T4 statement blocks here: http://msdn.microsoft.com/en-us/library/bb126509.aspx
The easiest way to think of the control code in a T4 template is by sewing it together in your head into one big program. The variable we declared previously on line 6 can be used in the T4 control code that follows it – and each of these if-else branches conditionally adds some text (shown in gray) to the outputted file. The if-else statements above are checking some properties exposed by our template host to the Add View templates.
Moving a bit further down, we see a curious statement block as follows:
We declare a variable called ‘properties’ of type ‘List<string>’. How are we able to use the List type in our T4 code? It is because of this import directive we saw at the top of the template:
Line 56 then calls a method called ‘FilterProperties’ – but where is this method located? It is actually defined near the bottom of the template:
If you look carefully, unlike statement blocks, this block of code begins with ‘<#+’. This is known as a class feature blocks, and they work like this: T4 takes all the class features in your template and adds them to the class that is compiled from your template behind the scenes. This isn’t just limited to methods, but also things like properties (anything that would normally go under a class). Like a member of a class in a regular code file, these now become accessible to the rest of your template’s code.
Read more about class feature blocks here: http://msdn.microsoft.com/en-us/library/bb126541.aspx
Our default templates use the ‘FilterProperties’ method from the above screenshot to by default output markup only for some properties in the type – namely ones that are public and would also be displayed in the designer by things like GridView. This logic is identical to what you would get from calling the GridView.IsBindableType method.
The ‘IsBindableType’ method called on Line 136 is defined a bit further down in the template:
If you wanted to change which properties our templates filter out, you can modify either of these methods to your liking.
Lastly, have a look at Line 65 below:
Here, we have a new construct that begins with ‘<#=’. This is called an expression block and it is used to insert values from T4 code into the outputted text. As you can see above, we have a foreach loop that is iterating properties and defines a variable local to the loop called ‘propertyName’. Because we want to output a <th> tag for each property name, we use an expression block referencing that variable.
You can find more about expression blocks here: http://msdn.microsoft.com/en-us/library/bb126508.aspx
As you can see, the various ways through which you can selectively output text in a T4 template make T4 a very powerful tool in getting the productivity of automatic code-generation with the flexibility of customized output.
MVC Tools T4 Host Properties
The Add Controller and Add View dialogs each expose different properties to templates through the template host, as demonstrated above in our breakdown of the Create template. Here is the full list of available properties exposed by our template host, for your template to use (Note: The exact names of these properties may change between the RC and the final version):
The name of the Controller class, including the ‘Controller’ suffix
The namespace into which the Controller is being generated
Indicates whether or not the user checked the option in the Add Controller dialog to get extra action methods
The name of the controller class without the ‘Controller’ suffix
The name of the view (without extension), as typed in the Add View dialog
The default namespace of the view’s parent folder
Evaluates to true if the user chose a partial view in the Add View dialog
Evaluates to true if the user is creating a view with a master page
Evaluates to true if the user is creating a regular view page
Path to the master page the user chose in the dialog (to be used only when IsViewContentPage is true)
Name of the primary content place holder into which the generated content will be placed. This is the content place holder id the user typed into the Add View dialog
A list of all content place holder ID’s in the master page, if a master page was chosen for this view
The output file’s extension (including the period)
This is a string that is used to output the generics clause for the ‘Inherits’ attribute in the view’s directive (for strongly-typed views). Example: “<MyType>” or “(Of MyType)”
This is a Type object representing the type to which a strongly-typed view is bound. It can be used to get information on the properties in the type and the like
Additionally, there are a few properties exposed by the default ‘Host’ property that you may find useful, such as the path to the template currently being executed. You can read more about those properties here: http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.texttemplating.itexttemplatingenginehost_properties.aspx
We hope this article has given you enough information about T4 templates and how to effectively use them in conjunction with the tooling Visual Web Developer provides for ASP.NET MVC. There is a lot more you can do with T4 than just what we went over in this post, so check out some of the information out there (Scott Hanselman’s post is a great place to start) and see what you can come up with. PLEASE post any comments, suggestions, or questions you have. Thanks for reading!
Abhishek Mishra | Software Design Engineer | Visual Studio Web Developer