T4 Tutorial: Reusing code generators on multiple projects
This article is a part of a series that introduces code generation using C# and Text Templates (also known as T4 Templates) in Visual Studio; explains how to create reusable templates and combine them in complex code generators. In order to follow examples in this article, you need to have Visual Studio 2008 Standard Edition or higher, SQL Server 2005, T4 Toolbox and T4 Editor installed on your computer.
Introduction
In the previous article of this series, we created CrudProcedureGenerator.tt – a reusable code generator that produces CRUD stored procedures for all tables in a given SQL Server database. Each type of stored procedure is produced by a separate template – DeleteProcedureTemplate.tt, InsertProcedureTemplate.tt or UpdateProcedureTemplate.tt. The Generator and Templates use SMO (SQL Server Management Objects) to retrieve table and column information from the database. We have also created a simple code generation file shown below to generate CRUD stored procedures for all tables in the Northwind sample database.
<#@ template language="C#" hostspecific="True" debug="True" #> <#@ output extension="txt" #> <#@ include file="T4Toolbox.tt" #> <#@ include file="CrudProcedureGenerator.tt" #> <# CrudProcedureGenerator generator = new CrudProcedureGenerator(); generator.DatabaseName = "Northwind"; generator.Run(); #>
In its current form, this code generation solution will work well for producing CRUD stored procedures in a single application development project. This article will illustrate how it can be reused across multiple projects and multiple teams.
Source control
It is important to keep both generated source code and code generation files under source control. During initial development of the code generation solution, the code generation files are typically stored side-by-side with the generated files.

However, as soon as the code generation files need to be reused outside of the original project where they were developed, they need to be stored separately and treated as a separate project.
Separate code generation files from project files
- Create a new folder called CodeGeneration and move reusable code generation files from the original project folder into the new folder.
Note that NorthwindStoredProcedures.tt remained in its original location. This file relies on reusable code generation files to generate SQL scripts specific to a particular project, Northwind database in this example. In other words, NorthwindStoredProcedures.tt is project-specific and should be stored with the rest of this project’s source code.
Use relative file paths
It is important to rely on relative file paths in team environment to allow for differences in computer configurations of different team members. However, after moving reusable code generation files out of their original location, we will get an error trying to regenerate NorthwindStoredProcedures.tt.
As you can see in the source code of NorthwindStoredProcedures.tt at the top of this article, it uses include directive with a relative path to pull in the contents of CrudProcedureGenerator.tt. Although we can fix the immediate problem by changing the NorthwindStoredProcedures.tt to refer to CrudProcedureGenerator.tt as “..\CodeGeneration\CrudProcedureGenerator.tt”, support for relative file paths in the include directive is limited just one level of nesting and it will not be able to find DeleteProcedureTemplate.tt, IncludeProcedureTemplate.tt and UpdateProcedureTemplate.tt included by CrudProcedureGenerator.tt. We can work around this limitation by adding path to shared code generation files to the list of known IncludeFolders.
- Add IncludeCodeGeneration string value to the HKLM\Software\Microsoft\VisualStudio\9.0\TextTemplating\IncludeFolders\.tt registry key. Enter full path to the CodeGeneration folder as the value data.

Visual Studio will attempt to resolve relative file paths used in the include directive by combining them with the known IncludeFolders. As long as each developer working on the project has the path to the CodeGeneration folder added to the registry, they will be able to use the shared code generation files without having to modify them or using absolute paths in files themselves.
Reusing code generator on another project
At this point, all we need to start using CrudProcedureGenerator on another project is create a code generation file similar to NorthwindStoredProcedures.tt and specify a different database in it.
<#@ template language="C#" hostspecific="True" debug="True" #> <#@ output extension="txt" #> <#@ include file="T4Toolbox.tt" #> <#@ include file="CrudProcedureGenerator.tt" #> <# CrudProcedureGenerator generator = new CrudProcedureGenerator(); generator.DatabaseName = "pubs"; generator.Run(); #>
The resulting structure of folders and projects in your solution may look as shown on the right. Complete source code is available for download at the bottom of the article.
Reusing code generator on another team
In this article we assumed that both projects using the shared code generator are stored in the same source control repository. This may be the case when the same team is working on both projects. If separate teams are working on these projects, they may be using separate source control repositories. Only one repository should be used for development and maintenance of the code generation files. This repository stores the “master” copy of the code generation files. Other teams will typically store a copy of these files in their own source control repository to preserve complete source code history of their project. As improvements are made to the master copy, other teams can upgrade their copy when it’s appropriate for their project.



The approach with the registry is great but it only seems to work if you use visual studio to do the generation (i.e. ctrl+s on the NorthwindStoredProcedures.tt). If you try and run NorthwindStoredProcedures.tt via the command prompt using TextTransform.exe, it doesn’t work.
Note in, the end I do not want to execute the templates via the command prompt, but I am creating my own wrapper around which I can trigger a generation via a thick client app.