This post is a part of the series that introduces code generation with Text Templates (also known as T4 Templates) in Visual Studio using C# and Visual Basic; 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 or later, T4 Toolbox and T4 Editor installed on your computer.
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#v3.5" hostspecific="True" debug="True" #> <#@ output extension="txt" #> <#@ include file="T4Toolbox.tt" #> <#@ include file="CrudProcedureGenerator.tt" #> <# CrudProcedureGenerator generator = new CrudProcedureGenerator(); generator.DatabaseName = "Northwind"; generator.Run(); #>
<#@ template language="VBv3.5" hostspecific="True" debug="True" #> <#@ output extension="txt" #> <#@ include file="T4Toolbox.tt" #> <#@ include file="CrudProcedureGenerator.tt" #> <# Dim generator As CrudProcedureGenerator = 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.
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 NorthwinProcedures.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, NorthwinProcedures.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 NorthwinProcedures.tt.
As you can see in the source code of NorthwinProcedures.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 NorthwinProcedures.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. On a 64-bit OS, this registry key is located under HKLM\Software\Wow6432Node.
- 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 NorthwinProcedures.tt and specify a different database in it.
<#@ template language="C#v3.5" hostspecific="True" debug="True" #> <#@ output extension="txt" #> <#@ include file="T4Toolbox.tt" #> <#@ include file="CrudProcedureGenerator.tt" #> <# CrudProcedureGenerator generator = new CrudProcedureGenerator(); generator.DatabaseName = "Pubs"; generator.Run(); #>
<#@ template language="VBv3.5" hostspecific="True" debug="True" #> <#@ output extension="txt" #> <#@ include file="T4Toolbox.tt" #> <#@ include file="CrudProcedureGenerator.tt" #> <# Dim generator As CrudProcedureGenerator = 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.
Code generators themselves are also code that you have to maintain. All coding best practices apply to code generators as well. In particular, it’s a good idea to store code generators in your source code repository; it’s a good idea to use relative path references and it’s a good idea to treat code generators as separate deliverables as soon as you start using them on multiple projects. T4 makes these tasks easier by allowing you to create code generators as pure text source files that don’t need to be compiled or have an installation program. It does have some limitations that present challenges, namely the resolution of relative file paths in the include directive. However, the workarounds are simple enough to manage.
Code generators are also software tools used by people. As such, they need to be easy to use. T4 Toolbox helps by allowing you to use a single code generator that produces multiple output source files and thus reducing the number of code generators you have to use in a given project. It also helps by adding generated output files to source control automatically and by checking them out when a regenerated output file changes.
One particular aspect that affects ease of use is error handling. As a code generator gets adopted by other people, it becomes increasingly important for it to handle error conditions in a manner that helps the users. This is the topic of the next article in this series.