The previous post I talked about the history of T4 Toolbox and some of the reasons why it was created. Let’s review what is actually in it from technical prospective. If you want to know complete details, take a look at the source code, which is installed in C:\Program Files\T4 Toolbox by default.
Micro-framework for composite code generators
T4 Toolbox includes several core classes that form a micro-framework for building composite code generators.
Template is a class that serves as a base for code generation templates, or more specifically, code generators that produce a single output file. Template implements the nested template class design technique, which provides the best balance between encapsulation, extensibility and the ease of its use. Template inherits from TextTransformation, allowing you to take advantage of the rich templating functionality of T4.
When implementing a concrete template, you need to define its code generation parameters as public properties or fields and override the abstract TransformText method. Inside of TransformText method you typically use text blocks, expression blocks, Write and WriteLine methods to implement the required code generation logic. You can also override the virtual Validate method to validate the code generation parameters, the check the environment for expected error conditions (is the database server available?) and report errors and warnings using Error and Warning methods. To use the concrete template, you need to create a new instance, assign the required properties and call its Render method. Render implements the template method pattern and calls the Validate and TransformText implementations you provide.
The Render method will write the content produced by the template to the standard output file. When this method is called for two or more templates, their output will be concatenated. To write template’s output to a separate file, you need to use RenderToFile method, which takes file name as a parameter.
Template is defined in T4Toolbox\Template.tt and referenced by including T4Toolbox.tt. Detailed example of its use is available here. We are currently considering removing the RenderToFile method and making the Render method write output to a separate file based on additional properties users can configure.
Generator is a class that serves as a base class for composite code generators, that use one or more Templates to produce multiple output files. Design of this class is similar to the Template class – it defines an abstract RunCore method, a virtual Validate method and a public Run method which implements the template method pattern. The difference is that Generator class does not inherit from TextTransformation and therefore not intended to have any content generation logic. Instead it is supposed to create one or more concrete template objects and render them from its RunCore method. To be consistent with Template class, Generator defines Error and Warning methods you can use for error reporting.
TransformationException represents expected exceptions that can occur during code generation. Both Template and Generator handle expected exceptions by displaying only their error message in the Error List window of Visual Studio. When implementing code generators, you can use TransformationException to wrap exceptions that occur due to expected error conditions and provide a brief, specific error message that will help users of the code generator to correct the error. Unexpected exceptions, on the other hand, are displayed with full stack dump and help the author of the code generator to troubleshoot the error.
TransformationContext is a static class defined in T4Toolbox\TransformationContext.tt. It encapsulates the integration code which uses Visual Studio extensibility APIs to manage generated output files as part of the Visual Studio project to which the main code generator (the T4 file) belongs. It also provides access to the transformation Host, has ResolvePath method you can use to convert a relative file path to an absolute path when accessing an external file from the code generator. TransformationContext also includes convenience properties and methods - Project, ProjectItem, FindProjectItem for accessing Visual Studio automation APIs from code generators. This is used, in particular, by the Enum SQL View generator to access the Visual Studio’s CodeModel API which is very handy if you want to generate code based on other code in your project.
Of all classes in T4 Toolbox, TransformationContext is the least fleshed out. It is defined in T4Toolbox\TransformationContext.tt and referenced by including T4Toolbox.tt.
Automatic management of multiple output files in the project
T4 Toolbox makes it easy to create a Generator that uses one or more Templates to produce multiple files from a single input or model. It also assumes that you are generating code at design time and uses Visual Studio extensibility APIs to automatically maintain the list of output files in the project and source control for you. When output files are regenerated, T4 Toolbox will automatically check out them out, but only those files that changed.
T4 Toolbox manages output files based on the assumption that a code generator simply reads information from the model, start to end, and generates all output files, every time. When a particular output file does not get regenerated, it assumes that the underlying definition was removed from the model and that the file is no longer necessary. For example, when a hypothetical CRUD stored procedure generator does not regenerate Products_Delete.sql we assume that Products table was deleted from the database, its stored procedures are no longer necessary and their source code should be removed.
Here is how this works. When a new output file is generated (see Template.RenderToFile method), TransformationContext automatically adds it to the project as an item nested under the .tt file (see SaveOutputToFile method). If the project is under source control, the new file will be automatically added to source control as well. When an existing output file is regenerated, TransformationContext will check out and overwrite the existing file, but only if its content changes. In the end of code generation, TransformationContext will delete from the project and the source control all old output files that were not regenerated.
Unfortunately, the automatic management of output files has a limitation. TransformationContext cannot distinguish between the standard transformation output file created by Visual Studio and custom output files that have the same name as the .tt. In order to avoid deleting standard output file from source control every time the generator runs, TransformationContext will not delete old output files if they have the same name (but not the extension) as the main .tt file. This may result in having obsolete output files that are no longer necessary. Needless to say, it best to avoid creating a custom output file with the same name as the .tt file in your code generator.
Unit test runner
T4 Toolbox allows you to create unit tests for your code generators. There are two parts to this. First, T4 Toolbox encourages you to design code generators to be testable, by encapsulating them in .NET classes and breaking down complex logic into smaller Template and Generator classes. Code encapsulated in classes is also easier to test because there is less shared state you need to worry about. In comparison, some of the available T4 documentation and examples encourage procedural design, which produces a lot of shared state and makes automated testing of code generators more difficult.
Second, T4 Toolbox includes a light-weight runner for automated tests based on the Visual Studio Unit-Testing Framework. The runner executes tests during template transformation. Simply create a .tt file, define a test class with a couple of test methods, include T4Toolbox\UnitTesting.tt, and your test methods will run automatically when you save the file. The test runner reports assertion failures in the Error List of Visual Studio and writes a detailed test execution log to the output file. Detailed example of unit testing of code generators is available here.
A special unit test runner is needed is because T4 engine compiles and runs the templates in memory. There is no .NET assembly to run tests against, which makes it practically impossible to use the traditional Visual Studio test projects to test T4 code generators. Alternatively, you could extract all logic you want to test out of your .tt files into a custom .NET assembly and then create a test project for it. However, T4 locks all referenced assemblies every time you transform a .tt file. You will not be able to recompile the custom assembly until you restart Visual Studio or transform 25 other .tt files, whichever comes first.
Custom directive processors
T4 Toolbox currently includes processor for a custom xsd directive. By adding this directive to your code generator, you can compile an XML schema into a set of .NET serialization classes, which allow accessing XML files as strongly-typed object hierarchies. We are currently using it to build a LINQ to SQL generator.
We may add more custom directive processors to T4 Toolbox as we discover problems that cannot be easily solved in code blocks.
Ready-to-use code generators
T4 Toolbox includes, admittedly limited, number of ready-to-use code generators. The current list includes a template for generating a SQL view from a C# enumeration and an incomplete LINQ to SQL generator. The LINQ to SQL generator will enable model-first development approach, where you define your entity model using standard LINQ to SQL designer and generate database schema, stored procedures, data context and entity classes from this model.
We will definitely add more ready-to-use generators to T4 Toolbox in the future.
Visual Studio project item templates
T4 Toolbox extends the list of project item templates available in the Add New Item dialog of Visual Studio. It adds a Code Generation folder with templates for common code generation tasks - File, Template, Generator, as well as the packaged code generators – Enum SQL View, LINQ to SQL.
These item templates are installed in the following folders by default:
C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplates\CSharp\Code Generation
C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplates\VisualBasic\Code Generation
T4 Toolbox allows you to build complex code generators that produce multiple outputs entirely in T4 files. Unlike with DSL and GAT packages, there are no additional assemblies to compile and deploy, no installation programs to write. You create a code generator as a set of T4 files, check them in your source control system. All your team needs to do is simply get the latest version of project’s source code.
T4 Toolbox framework is compiled in T4Toolbox.dll and supports both C# and Visual Basic as template language for implementing code generators.
T4 Toolbox was designed to support design-time code generation, as opposed to build-time or run-time. It relies on Visual Studio extensibility APIs to manage output files and allow advanced code generation scenarios. However, this also limits you to generating code only in Visual Studio. Additional work would need to be done in T4 Toolbox before it can support build-time code generation. Although run-time code generation would be even easier to support, T4 itself currently requires Visual Studio and doesn’t support this scenario.