T4 Templates in Visual Stuido

There are not many places on the web to understand T4.  As I have been poking around T4 lately here are a few notes I have gathered together.  One thing of interest is Razor Generator I have talked about before.  It can be used as a T4-like preprocessor template although I feel like it is less mature.

Output Multiple Files

Generating multiple files from a single template seems to be a fundamental need in the community.  I personal prefer multiple files because you can see inclusions and deletions quickly in the git log instead of having to do the extra step of a diff.  Some folks think that it shouldn’t be done because it doesn’t work well.  Despite it being such a simple thing, it seems everyone has a solution, or at least an opinion.  As always I want to reuse the components closest to the core.  So I start with reusing the EF File Manager.

Entity Framework has a nifty file manager that you can use even if you are not using EF in the assembly you are generating in.  You start by adding `<#@ include file=”EF.Utility.CS.ttinclude” #>` to the beginning of your .tt file.  Then create an instance via `var fileManager =EntityFrameworkTemplateFileManager.Create(this);`.  Then every time you want to start a new file you simply call `fileManager.StartNewFile(newFileName);`.  Then at the end tell it to process by calling `Process()` on the `fileManager` object.

This method works fine, but I had trouble with my files disappearing every other time I hit save.  That is the first time I execute the script, it makes the file.  The second time I execute the script the file is deleted.  The third time I execute the script it is generated again.  If anyone has a solution to that, leave me a comment.

To investigate further I decided to try out the Tangible T4 template called TemplateFileManagerV2.1. It is intended to operate simularly: include `TemplateFileManagerV2.1.ttinclude`, instantiate `var fileManager = TemplateFileManager.Create(this);` and use it the same way.  The down side was it had the same problem.

Then I tried the DamienG  (git hub) solution with the same results.  That version really only adds a `fileManager.EndBlock()` after each new file content.

The next step was to go completely low tech with Oleg’s SaveOutput() function.  The issue there is that it doesn’t add/remove the file to/from the solution.

Since T4 Toolbox conflicts with Tangible T4 editor that I was using at the time, the T4 Toolbox option was the last I tried.  It is also the most different.  The documentation pushes you to use it’s template class method.  This isn’t much of an issue but it is a change.  The first step is to create a T4 class that extends `Template` with your output being encapsulated within a method called `TansformText()`.  Because of scope, you need to pass in any values you need to the constructor of the class or otherwise assign it to the instance.  Once your class is created, to create output to a particular file, you need to set the property `<instance>.Output.File` to the your file name.  Then you call `Render()` on your instance.  This method was predictable, but it took away my T4 Editor since Tangible is the only T4 editor for VS 2015.

Since I want an independent solution, and I like my T4 editor at the moment (I will investigate others as they become available for VS 2015) I returned to Oleg’s post and downloaded the source for `MultiOutput.tt` and tried it.  To my delight it worked by simply calling `SaveOutput(<filename>)` at the end of the file content as described in the blog post mentioned above.  But did it have the issue where the file disappeared every other time I hit save? Not at all after extensive testing.  Thus this is the solution I am using currently and it seems to be rock solid.

I like the template method used in the T4 Toolbox.  It seems a little cumbersome at first, but it brings the added benefit of template reuse and decoupling the template from the manor you are acquiring the objects you are generating the template with.  One could also create a custom class for passing into the template to further obfuscate the result from how you acquire the source data.  At some time in the future I may do this and start putting my templates up on Github to share.  If you are interested let me know.

Generation Based Existing Classes

Often you may want to generate factories, decorators, bridges, facades or just interfaces for existing generated files.  One proven method is to use System.Reflection to iterate over the types.  This requires a complied assembly.  You can use the one in the current project or use an external one.  Once you have the path to the assembly you can call `Assembly.LoadFile(<dll.path>)` to load the assembly and then call GetTypes() on that to iterate over the public types.  This looks something like this:

var assembly = Assembly.LoadFile(location);
foreach (var type in assembly.GetTypes())
   
{
    ... Do stuff here with the type
}

Get The Current Namespace

Something commonly done is outputing of namespace information into a generated class.  To get the current namespace:

string currentNamespace = System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint").ToString();

More

As I find more I will share them here.  If you have any you would like to share, leave them in the comments below.

Advertisements
T4 Templates in Visual Stuido

4 thoughts on “T4 Templates in Visual Stuido

  1. Hey, I’m the guy that wrote the article about the multi-file outputs. 🙂

    I’ve revisited the problem again after a few months and still not found a solution that works well, especially with the thing failing every other time. Are you still satisfied with Oleg’s method? I’m definitely considering trying it, as the massive file is getting increasingly painful and I want to generate some other pieces that really can’t be all in the same file. How’s the debugging experience with that toolchain? That was the other main problem I had – I couldn’t actually step into the template execution without really screwy visual studio behavior.

    I’m really hoping the T4 ecosystem gets better. I think it has tremendous promise. Of course, I haven’t yet tried it in VS 2015 yet, so it could be a bit more stable there than I experienced in 2013 as well.

    1. drydenmaker says:

      I have given up on the Tangible T4 editor and made the jump to ReSharper. The editor still isn’t perfect (has trouble resolving external types) but atleast I can use T4 Toolbox. Extending the template class makes things much cleaner and it is even easy to generate files in other projects. The debugging experience with VS2015/ReSharper and T4 Toolbox is good enough, much better than 2013. I feel like T4 is like SVG as a neglected technology.

  2. Came across your article and couldn’t help but commiserate as I am struggling with the exact same conclusions. T4 does not appear to be ready for prime time with half-baked implementation and little to no recent documentation, examples or support. Very frustrating to say the least.

    I was wondering how you got around T4Toolbox not auto including files in projects?

    1. drydenmaker says:

      On the template you can set the Output.Project member to the path to the csproj file.

      T4 keeps almost becoming a first class citizen. I am using R# with ForTea to get a little closer to where I want to be. With Core and Roslyn combined with ScriptCs and the new Visual Studio enhansements I think we are looking at a world where we use C# Scripting to inspect the solution code in Roslyn and output generated classes via Razor. I lack the time to drive such a project but you can bet that I will keep putting it out into the universe and contribute as it buds.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s