Thursday, June 17, 2010

Working with the T4

Update: Thanks to Tim Fischer from Tangible, I got to solve all the problems described in the post below using VolatileAssembly and macros like $(SolutionDir) or $(ProjectDir).

When T4 (Text Template Transformation Toolkit) appeared as a third party toolkit that you could install on Visual Studio 2008, I thought to myself that it is a cool concept, but I didn't get to actually use it. Now it is included in Visual Studio 2010 and I had the opportunity to use it in a project.

The idea is to automatically create code classes and other files directly in Visual Studio, integrated so that the files are generated when saving the template. All in all a wonderful idea... but it doesn't work. Well, I may be exagerating a bit, but my beginning experience has been offputting. I did manage to solve all the problems, though, and this is what this blog post is about.

First of all, there is the issue of intellisense. I am using ReSharper with my Visual Studio, so the expectations for the computer knowing what I am doing are pretty high. In the .tt (the default extension for T4) files you don't have any. The solution for this is to use the Tangible T4 editor (I think they were going for a fifth T here) that comes as a Visual Studio addon for VS2008 and VS2010. Fortunately, there is a free version. Unfortunately, it doesn't do intellisense on your own libraries unless you buy the priced one. Also, the intellisense is years behind the one provided by ReSharper or even the default Visual Studio one and the actions one can do automatically on code in a T4 template are pretty limited.

The second problem was when trying to link to an assembly using a relative path to the .tt file. The Assembly directive supports either the name of an assembly loaded in the GAC or a rooted path. Fortunately, the VS2010 version of the T4 engine supports macros like $(SolutionDir). I don't know if it supports all Visual Studio build macros in the link, but the path ones are certainly there.

Here is how you use it. Instead of

<#@ Assembly Name="Siderite.Contract.dll" #>

<#@ Assembly Name="$(SolutionDir)/Assemblies/Siderite.Contract.dll" #>

The third problem was that using an assembly directive locked the assembly used until you either reopened the solution or renamed the assembly file. That proved very limiting when using assemblies that needed compiling in the same solution.

This can be solved by installing the T4 Toolbox and using the VolatileAssembly directive. Actually, on the link above from Oleg Sych you can also find a bit advising using the T4 toolbox VolatileAssembly directive in the Assembly Locking section.

Here is how you use it. Instead of

<#@ Assembly Name="$(SolutionDir)/Assemblies/Siderite.Contract.dll" #>

<#@ VolatileAssembly
name="$(SolutionDir)/Assemblies/Siderite.Contract.dll" #>
As you can see you need to specify the processor (the VolatileAssemblyProcessor would have been installed by the T4 Toolbox) and you can use macros to get to a relative rooted path.

So thanks to the eforts of Oleg and Tim here, we can actually use T4. It would have been terribly akward to work with the solution in the obsolete section below. The Tangible guys have a learning T4 section on their site as well. I guess that using the resources there would have spared me from a day and a half wasted on this.

The following is obsolete due to the solutions described above, but it is still an informative read and may provide solutions for similar problems.

Click to expand.

Tips And Tricks:
Problem: the T4 generated file has some unexplained empty lines before the start of the text.
Solution: Remove any spaces from the end of lines. Amazingly so, some white space at the end of some of the lines were translated as empty lines in the resulting .tt.

Problem: The code is not aligned properly
Solution: Well, it should be obvious, but empty spaces before the T4 tags are translated as empty spaces in the resulting .tt file. In other words, stuff like <# ... should not be preceded by any indenting. It will make the template look a bit funny, but the resulting template will look ok. If you dislike the way the intending looks in the template, move the indent space in the tag, where it will be treated as empty space in the T4 code.


Tim Fischer said...

Hi i am not 100% sure but it seems like you might be interested in the fact that starting with VS2010 and tangible T4 Editor V1.9 you can actually use Build Macros to point to relative files

<#@ assembly name="$(ProjectDir)\myassembly.dll">

That is the new syntax to use for relative stuff since VS2010. Also works for imports.

For the file lock issue the solution is to close and reopen the solution. You do not need to restart VS but unfortunately VS itself locks the dll until you close the solution.

Tim Fischer said...

Another solution for that DLL locking issue is to use the <#@ VolatileAssembly name="mydll.dll"#> directive from Oleg's t4toolbox which is also supported in the T4 Editor from tangible.

Sorry that all this is not that obvious.

Siderite said...

Hey, thanks for the comment! I will try these things today and tell you how it goes.

It seems to me that googling for T4 brings results that are more relevant to the 2008 version and many of the 2010 features are hard to get to.

Tim Fischer said...

Yeah that's right. We are trying to build up samples for VS2010 in the build-in template gallery [see our blog] and also here on that page (2008/2010 mixed however) so you do not need to crawl the web all the time. Indeed i actually crawl the web each day and then try to aggregate things here:

Siderite said...

Thanks, Tim. Your solutions worked great and I have updated the blog post accordingly. Wish I would have spent more time reading the series on the Tangible site, but since it looked so commercial I went for the blogs I could Google. My mistake.