T4 templates: the lazy programmer’s friend
Text Template Transformation Toolkit (T4 templates) are a very powerful asset when used appropriately. Their main advantage is their ability to generate large amount of repetitive code over and over, at the click of a button and with a Swiss watchmaker’s precision.
My current project, Responder, an Outage Management System, contains dozens of xml files to define its data model and for configuration. Until a few months ago, adding a new column to a data table was a several hours process. It involved a lot of manual editing of XML files, updating SQL scripts, updating of code classes used to manipulate data, etc.. Errors were frequent and the simplest typo could prove dramatic if not caught on time by manual testing.
This all started changing with a T4 template. By generating all the necessary code straight out of the XML file, we removed most of the hand-coding, making the code (our RxDb.* and ArchivesDb.* structures for those who know) a perfect translation of the content of the original XML file. In the process, we have found typos and missing code that slipped through the QA cracks.
This change to code generation was started a couple of years ago. Now, other team members have embraced the T4 power to generate business objects to manipulate data in our complex DataSets. This has allowed us to update our data schema with minimal friction and add a lot of convenience methods to all our business objects with just a couple of line of actual code change
Of course, T4 templates have their own complexity and can be hard to navigate as they are a mingling of template and code. This becomes even more blurry (literally) when you are generating a C# file using C# as the template language. But, with the right tools, you can make more sense out of this funky looking piece of code/template.
Here is the MSDN main documentation for T4 templates, fully supported in VS2010. You can find the supporting source code for this post here. I have to apologize for dummying down the attached code, but I wanted to ensure that it did not contain any proprietary information. I took the safe route and took out any business-related terms. I just kept the basic structure.
Don’t forget to download the free tangible T4 Editor for VS2010. There are plenty very valuable resources online which will guide you through your first steps.
But here are some pointers that can help you keep things clean.
The first pointer I will give you is regarding accessing local but relative resources (such as XML files). Feel free to visit StackOverflow and read through my question and the answer here. Basically, if you want your template generating code to be aware of its surroundings, you must set the hostspecific flag to true AND use the Host class to access host-specific information. This makes your template portable.
Here is the tag declaration:
<#@ template language="C#" hostspecific="true" #>
And here is the use of the Host class:
XmlFile = Host.ResolvePath(@"..\Data\Schema.xml")
The following pointer was not in the original post but came after several months of using more and more T4 templates. We started realizing that, as we add more code to the generated code, our templates were getting pretty hard to read. We were able to simplify some of our templates by using some of the following techniques:
- Abstract base class: whether you are generating a single class or many classes, putting some of the common code into an abstract base class will keep your template focused on the code that needs to be generated
- Extension methods: introduced with C# 3.0, those allow you to add public methods to existing classes without getting access to the extended class. There are some restrictions with this approach, but it will still allow you to separate code that must be generated from code that can stay static from one code-gen to another
- Partial classes: the best approach in my opinion. This allows you to put all your static code in one or more code file and leave only the generated code in your T4 template. And the static code can include private members, helping you keep your objects nicely encapsulated
Of course, the best approach is a good mix of all 3 of them. Try to keep each code file, be it template, extensions, partial or base class, as easy to read as possible. The attached code sample contains both an abstract base class and partial classes.