Tutorial: Generate seperate files from a T4 Template

When you run the custom tool of a T4 Template, it generates code in a child node of that template. On StackOverflow I have seen a a lot of devs asking how to write to separate files, or even to a different folder. This is actually easy to achieve, and there are multiple possible solutions. In this article, I will address how I achieved this in my TexTran projects.

Generation environment

In order to have control of the output, we must know how to fetch it. You can access the currently output by calling this.GenerationEnvironment inside <# Control blocks #> . But what is GenerationEnvironment ?

TextTemplate.GenerationEnvironment is the string builder that generation-time code is using to assemble generated output

MSDN

Write to separate file

So, this.GenerationEnvironment is basically a stringBuilder holding the generated string that is our output. We have to write this a new file. For this, just add this simple method to your T4 <#+ Class Feature Block #> .

public void SaveFile(string folder, string fileName, string content)
{
	using (FileStream fs = new FileStream(Path.Combine(folder, fileName.Trim() + ".cs"), FileMode.Create))
    {
        using (StreamWriter str = new StreamWriter(fs))
        {
            str.WriteLine(content);
            str.Flush();
        }
    }
}

This is a simple method that takes the desired path, file name and the filename and uses a StreamWriter object to write your files to a specific location.

Inside your T4 Template, at the bottom (under the last output block) you can add the following lines

<#
	SaveFile(path, fileName, this.GenerationEnvironment.ToString());
	this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length);
#>

What this does, is it writes the string content of the GenerationEnvironment StringBuilder to the path specified. Then we remove the full string value from the GenerationEnvironment so that there is no output left for the transformer to write to the child node.

Generate multiple files

One template can generate multiple files. In my TexTran example, I generate multiple Entities from a definition file.

Entities.tt:

... //imports 
<#
//Get current directory
var directory = Path.GetDirectoryName(this.Host.TemplateFile);
var entitiesPath = @"\ProjectName\FolderName";
var regExFilter = "(?<=src).+$";
var entitiesFolder = Regex.Replace(directory, regExFilter, entitiesPath);

// GenerateEntities method is in TransforManagers.ttinclude
List<EntityDefinition> entities = GenerateEntities(entityLines); 

if(entities != null)
{
	foreach (var entity in entities)
	{
#>
// This file is auto generated. Changes to this file will be lost!
using System;
using System.Collections.Generic;
using TextTran.Transformations.Enums;
	
namespace TexTran.Data.Abstractions.Entities
{
<#
	if (!string.IsNullOrEmpty(entity.Summary))
	{
#>
	/// <summary>
	/// <#= Regex.Replace(entity.Summary, "// ", "") #>
	/// </summary>
<#
	}
#>
	public class <#= entity.Name.Trim() #> : BaseEntity
	{
	<#
	foreach (var property in entity.Properties)
	{
		if (property.Nullable)
		{
#>		
		public <#= property.Type #>? <#= property.Name #> { get; set; }
<#
	}
		else
		{
#>		
		public <#= property.Type #> <#= property.Name #> { get; set; }
<#
		}
	}
#>
	}
}
<#
	SaveFile(entitiesFolder, entity.Name, this.GenerationEnvironment.ToString());
	this.GenerationEnvironment.Remove(0, this.GenerationEnvironment.Length);
	}
}
#>

The template above will write a file for each Entity iterated, using the entity.Name property for the filename. Call the SaveFile method at the end of the foreach loop. I added some lines on top that show how I create the path where I want to write to. In my TexTran project, this is located in the TransformSetup.ttinclude file.

Appendix

Just a small note. What would happen if one if your files name changes? It would keep the old file, and write a new one next to it. Add following method to your Class Feature Block, it removes all content from the destination folder:

public void RemoveFilesFromFolder(string path)
{
	Array.ForEach(Directory.GetFiles(path), File.Delete);
}

Simply call this method first in your template, so that the folder is cleared everytime you start re-generating your files.

I hope this tutorial helps! Please leave any feedback or questions in the comments below.

All templates used referenced in this article can be found here.