Using T4 to Create an AppSettings Wrapper, Part 4
In the previous article we finished the basic appsettings template that allowed us to generate a strongly typed class from the settings in a configuration file. We now want to expand the template to allow it to be customized depending upon the needs of the project, a later article. Before we get there though it is time to refactor the code to make it more reusable and easier to maintain. That is the focus of this article.
Update 28 Aug 2016: Refer to this link for updated code supporting Visual Studio 2015.
T4 Toolbox
Under the hood a template is nothing more than a compiled class that implements a method to render the template. Up to this point we have been using the default class because our template wasn’t that complicated but we want to be able to customize the template so it is time to move away from the default settings. The T4 Toolbox is a handy set of extensions to T4 that allows us to work with the template as a class rather than as a text file. By switching to a class approach we can take advantage of all the features we are used to in normal code but still ultimately generate a template. There is nothing we are going to implement that you couldn’t do by hand or with another library but the T4 Toolbox makes things easier so we’ll use it. You’ll need to download and install it before moving on.
To make the best use of T4 Toolbox we need to move our template into a class that derives from Template. We can then implement the TransformText
method to generate our template. First ensure that the T4 Toolbox is installed through Extension Manager. Normally if you’re creating a new template you will add a new template file using T4 ToolboxTemplate but since we already have one we’ll just modify our existing template file.
Including Files
T4 allows you to include text files inside templates using the include directive. It works similar to C++ #includes in that the contents of the file are inserted into the template file whereever the directive resides. This is useful for sharing common functionality across multiple templates. The included file must be either in the same directory as the template, a directory relative to the template or in a path that T4 will search.
Open the template file and add an include for the T4 Toolbox.
<#@ include file="T4Toolbox.tt" #>
This include brings in the infrastructure needed by T4 Toolbox. I normally place it after the assembly directives but before the import directives. A word of warning, T4 complains if it finds multiple assembly or import statements for the same string so try to keep these directives down to a single file.
Creating the Template Class
Now it is time to move the template into a Template class. Right after the import directives start a new class block. Define a new class that derives from CSharpTemplate
. The class name is not that relevant.
<#@ extension=".generated.cs" #> <#+ public class AppSettingsTemplate : CSharpTemplate { } #>
Move any methods from the original template inside the class body. They should probably be private since they are used only for generating the template. The last method in the class should be the override for the TransformText
. You can generate the template body by making method calls but the easier approach is to simply end the class block and starting the template text. After the template text will be another class block that finishes the TransformText
method. The call to GenerationEnvironment
is what returns the template text from the method. The template text outside of the class block is automatically added to the returned text.
public override string TransformText () { var className = MakeIdentifier(Path.GetFileNameWithoutExtension(Host.TemplateFile)); #> /* * This file is auto-generated from a config file. * Do not modify this file. Modifications will be lost when the file is regenerated. */ ... <#+ return this.GenerationEnvironment.ToString(); } } #>
Remember that indentation and blank lines can be confusing in templates so it might be necessary to play around with the template to get them right. The goal isn’t to make the template be styled correctly but rather the generated code.
Notice that the className
variable that was in a statement block before has been moved inside the method. Variables defined in the method are accessible to the template text. Also note that any statement blocks used inside the template text need to be switched to class blocks because a statement block cannot follow a class block.
Removing Unneeded Code
When we were writing the template in previous articles we defined a few helper methods. Some of these can go away because T4 Toolbox provides them for us.
MakeIdentifier()
was used to create a valid identifier given a string. It can be replaced by a call to PropertyName()
if you want a Pascal cased identifier or FieldName()
if you want a camel cased identifier. Replace the calls and remove the method.
GetNamespaceForTemplate()
was used to determine the namespace to use for the type. It can be replaced by a call to the property DefaultNamespace
which should provide the same information. Replace the calls and remove the method.
Host
was used to access the host information. T4 Toolbox wraps the host to better provide isolation from the underlying implementation. Replace references of Host
with TransformationContext.Current.Host
.
Nested Templates
If you run the template at this point you’ll see that the generated file is empty. What’s going on? If you look at your template file you’ll realize that all you’ve done is defined a template class. There isn’t anything that is actually causing the template to run. Time to fix that.
A nested template is a template inside a template. It is one of the key techniques that we can use to both encapsulates a template and make it configurable. The issue with the current template is that someone would need to know how the template works before they could customize it. Additionally they would need to change the template file to make any customizations. If we later release a new version of our template then they would either need to manually merge the changes in or redo their customizations. A nested template allows us to expose a simple template that exposes the customizable parts of the template (generally through properties) while calling the real template to do the actual work. This allows us to separate the real template from the customizable parts. Let’s break our template up into the customizable part and the template part.
I like for my customizable template to bear the name of the template as will be shown in the Add New Items dialog and the real template to match the template class name so rename the existing template file to AppSettingsTemplate.tt
. Then create a new template file called AppSettings.tt
which will be the customizable part.
The customizable template represents what will get generated so it needs the template directive, the output directive and an include of the real template.
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ output extension=".generated.cs" #> <#@ include file="AppSettingsTemplate.tt" #> <# #>
The real template doesn’t need the template or output directives anymore. In fact it would be an error to leave them in. Instead it just defines the assemblies and namespaces needed to generate the template. Additionally we need to tell VS not to treat it as a template file anymore so view the properties of the file and remove the custom tool setting. A caveat to this is that VS will not automatically regenerate the output if you modify this template anymore. If you make any changes to it you will need to regenerate it manually.
The customizable template needs to create an instance of the real template, call any customization members that it needs to and then render the template. Since we have no customization yet it would boil down to this.
<# var template = new AppSettingsTemplate(); //Do customization here template.Render(); #>
We’ve now set the stage to allow our template to be customized. In the next article we’ll expose customization points to make our template truly useful in real world projects.
Download the Code
Hello, Neat post. There’s an issue along with your website in ietrnnet explorer, would check this? IE still is the marketplace chief and a large element of people will omit your excellent writing because of this problem.
Normally I do not learn article on blogs, however I would like to say that this write-up very compelled me to try and do so!
Your writing taste has been amazed me. Thank you, quite
great article.
Hey There. I found your blog using msn. This is an extremely well written article.
I’ll be sure to bookmark it and come back to read more of your useful information. Thanks for the post. I’ll
certainly comeback.
Highly descriptive blog, I enjoyed that bit.
Will there be a part 2?
Good web site you have here.. It’s hard to find excellent writing like yours these days. I honestly appreciate individuals like you! Take care!!
Thanks for sharing your thoughts about C#. Regards
Hello there I am so delighted I found your site, I really found you by accident, while I was browsing on Yahoo for something else, Nonetheless I am here now and would just like to say thanks for
a remarkable post and a all round exciting blog (I also love the theme/design), I don’t have time to go through
it all at the minute but I have bookmarked it
and also added your RSS feeds, so when I have time I will be back to
read more, Please do keep up the excellent work.
Greate article. Keep writing such kind of information on your page.
Im really impressed by your site.
Hello there, You’ve done an incredible job. I will definitely digg it and in my view suggest to my friends. I am confident they will be benefited from this site.
I’ve been exploring for a little for any high-quality articles or blog posts in this sort of area . Exploring in Yahoo I at last stumbled upon this web site. Reading this information So i’m happy to show that I have an incredibly excellent uncanny
feeling I discovered just what I needed. I such a
lot definitely will make sure to don?t put
out of your mind this website and provides it a look regularly.
Great post. I used to be checking continuously this blog and I am inspired!
Very helpful info specially the closing phase 🙂
I deal with such information a lot. I was seeking this certain info for a long time.
Thanks and good luck.
I have been surfing on-line more than 3 hours these days, but
I never found any fascinating article like yours.
It is beautiful worth enough for me. Personally, if all website owners and bloggers made
excellent content as you probably did, the web
will be a lot more useful than ever before.
This website was… how do I say it? Relevant!! Finally I’ve found something that helped me. Kudos!
Superb, what a web site it is! This blog gives valuable facts to us,
keep it up.
My brother suggested I may like this web site. He used to
be entirely right. This post actually made my day. You can not consider simply how much time I had
spent for this information! Thank you!
Pretty nice post. I just stumbled upon your weblog and wanted to mention that I’ve truly loved surfing around your weblog posts. In any case I will be subscribing on your rss feed and I hope you write again very soon!
Hiya! I just would like to give an enormous thumbs up for the great data you’ve right here on this post.
I might be coming again to your weblog for
more soon.