T4 Tutorial: Creating your first code generator


This is the first post in a series that introduces code generation using C# and Text Templates (a.k.a. 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.

In this series of articles, we will create a code generator produces CRUD stored procedures for tables in a SQL Server database. In the examples, we will be using the Northwind sample database.

Creating a Code Generation File

  • Create a new C# Class Library project in Visual Studio.
  • Click Project->Add New Item in the main menu and select Code Generation->File template in the dialog.

Add New Item Dialog

  • Enter CrudStoredProcedures.tt as the item name and click the Add button.
  • Double-click the new file in the Solution Explorer. You will see the following text in the text editor.
<#@ template language=C#hostspecific=Truedebug=True#>
<#@ output extension=txt#>
<#@ include file=T4Toolbox.tt#>
<#
// <copyright file=”CrudStoredProcedures.tt” company=”Your Company”>
//  Copyright © Your Company. All Rights Reserved.
// </copyright> 

#>

This file is a Text Template, also known as a T4 Template. Text Templates are code generators that can be used to generate any text files, including C#, Visual Basic, SQL and XML. Text Templates use ASP.NET-like syntax and consist of text blocks, code blocks and directives. Text blocks are blocks of text in the template that are copied to the output file as is. Directives provide instructions to the text templating Engine on how to process the template. In the example above, template directive tells the Engine that this template uses C# in code blocks. Code blocks contain C# or Visual Basic code that runs during template transformation and allow making generated output dynamic.

Generating Static Output

We will start implementing our code generator by hard-coding DELETE stored procedure for the Products table of Northwind database.

  • Change CrudStoredProcedures.tt to look like so.
<#@ output extension=SQL#>
create procedure Products_Delete
    @ProductID int
as
    delete from Products
    where ProductID = @ProductID

Solution Explorer When you save CrudStoredProcedures.tt, the text templating Engine transforms it to generate the output file. In the Solution Explorer, the output file appears nested under the the file. You can also transform the file by right-clicking it in the Solution Explorer and selecting “Run Custom Tool” from the context menu. If you have multiple text templates in your project, you can transform them all by clicking “Transform All Templates” button in the toolbar.

In the template above, the text block is shown in gray. If you double-click the generated file, its contents will be identical to the contents of the text block and will look like so.

create procedure Products_Delete
    @ProductID int
as
    delete from Products
    where ProductID = @ProductID

At this point, the generated output is static, which is not any better than coding this stored procedure by hand. Instead, we can generate it dynamically, using database schema information provided by SQL Server.

Adding .NET code to text template

  • Change CrudStoredProcedures.tt to look like so
<#@ template language=C##>
<#@ output extension=SQL#>
<#@ assembly name=Microsoft.SqlServer.ConnectionInfo#>
<#@ assembly name=Microsoft.SqlServer.Smo#>
<#@ import namespace=Microsoft.SqlServer.Management.Smo#>
<#
    Server server = new Server();
    Database database = new Database(server, “Northwind”);
    Table table = new Table(database, “Products”);
    table.Refresh();
#>
create procedure Products_Delete
    @ProductID int
as
    delete from Products
    where ProductID = @ProductID

This code uses template directive to specify that this template uses C# in its code blocks.  This template contains a statement block which is defined using special markers - <# and #>. This block uses SQL Server Management Objects (SMO) to retrieve metadata information about Products table from the Northwind database running on the local SQL server instance. This API (SMO) is defined in a .NET assembly, Microsoft.SqlServer.Smo which is installed in the Global Assembly Cache by SQL Server setup program. In order to use this API, this template uses an assembly directive to reference the assembly where it is defined and an import directive to specify the namespace where Server, Database and Table types are defined without having to use fully-qualified type names.

Making Code Generation Dynamic

Having metadata information about the target table, we can now generate the DELETE stored procedure dynamically.

  • Change CrudStoredProcedures.tt to look like so.
<#@ template language=C##>
<#@ output extension=SQL#>
<#@ assembly name=Microsoft.SqlServer.ConnectionInfo#>
<#@ assembly name=Microsoft.SqlServer.Smo#>
<#@ import namespace=Microsoft.SqlServer.Management.Smo#>
<#
    Server server = new Server();
    Database database = new Database(server, “Northwind”);
    Table table = new Table(database, “Products”);
    table.Refresh();
#>
create procedure <#= table.Name #>_Delete
<#
    PushIndent(”\t”);
    foreach (Column column in table.Columns)
    {
        if (column.InPrimaryKey)
            WriteLine(”@” + column.Name + ” ” + column.DataType.Name);
    }
    PopIndent();
#>
as
    delete from <#= table.Name #>
    where
<#
    PushIndent(”\t\t”);
    foreach (Column column in table.Columns)
    {
        if (column.InPrimaryKey)
            WriteLine(column.Name + ” = @” + column.Name);
    }
    PopIndent();
#>    

This template uses expression blocks to dynamically generate name of the target table in the output file. Expression blocks are defined using special markers - <#= and #> and can contain any valid programming expression, which will be converted to string and written to the output file. This template also uses additional statement blocks to iterate through the list of Columns and calls WriteLine method to generate stored procedure parameter declarations and WHERE clause for the DELETE statement based on the primary key of the table. PushIndent and PopIndent methods are used to format parameter declarations and the where clause.

Conclusion

The text template shown in this article generates a single DELETE stored procedure based on table schema information retrieved from SQL Server using SMO. In the next article, we will talk about troubleshooting code generation errors.


Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

I already installed T4 Editor from Clarius and T4 Toolbox, but I don’t have the all items under the CodeGeneration as you show in your first figure. The only items available are “Enumeration with a SQL View” and “LINQ to SQL Schema”. I am using VS2008 Team Suite Edition.

What I have to do to have all the items?

Thanks in advance

I’m sorry about that Bruno. You probably got version 8.8.17.1 installed. I uploaded a new release to CodePlex this morning, but didn’t make it default until now. Could you install version 8.8.31.1? This should make the other templates available in the Add New Item dialog.

Hi again, Oleg!

Is there still no way to assign values to template’s properties right before rendering the template?

Actually, I use templates for code generation in runtime, not in Visual Studio. Now I use CodeSmith engine, but for my open source project I’d like to have at least a parametrized template renderer that is free.

The best way is to convert CodeSmith templates into T4 templates on-the-fly (it seems to be possible) as there is no suitable T4 editor in sight. But all my unfortunate creatures with custom hosts did not leave a chance to keep CodeSmith templates unchanged.

Maybe you have any ideas about some workaround here?

Alex,

I blogged about passing parameters to a template from an external tool a couple of months ago. Check out http://www.olegsych.com/2008/04/t4-template-design-standalone-template/.

To answer your second question, I don’t think translating CodeSmith templates on the fly is viable. They are different enough to make such translation very complex. On the other hand, creating T4 templates from scratch is easy. I would guess that the effort required to recreate CodeSmith templates in T4 is less than the effort required to create an automated translation tool.

Hope this helps

Interesting! It looks a bit like codesmith templates actually…

Oleg,
I wrote an extension to the string object and I am trying to use it in a .tt file but I get an error. Does .tt recongize custom extensions?

John, if I understand you correctly, you have an extension method you want to use in template code. You will need to set language parameter of the template directive to “C#v3.5″ or “VBv3.5″, otherwise T4 engine will use an older version of the compiler that doesn’t support extension methods.

This seems like a great tool - I wish it was available for Visual Basic.Net.

Thanks Corey. You can also use T4 with Visual Basic, simply change language parameter of the template directive to “VB” and write Visual Basic code in the code blocks.

Oleg, could you, please, clear one thing to me? I’ve just tried to re-produce your steps. How can I get CodeGeneration selector when adding new item? I use VS2008, I have Visual Studio SDK installed, but there is no CodeGeneration template. What are other requirements?

Thank you.

Sorry about my previous question. No problem now, just a great surprise “How simple” :) Thanks for these tutorials.

Oleg,

Sorry I haven’t responded. I have been extremely busy and this is the first time I have had time to get back into T4.

Thank you the solution of my extensions. I will try that and let you know the outcome.

Also, I understand you will be speaking at the Tallahassee .Net User’s Group in December. I look forward to your session and hope I have enough knowledge of T4 to discuss this methodology with you.

Thanks,

John

You can pass in a server name and instance to the Server constructor.
In my case it was Server server = new Server(”.\\SQLEXPRESS”);

After downloading v8.12.27.1 of the T4Toolbox and running it, the installer reports that VS2008 must be installed first. However, I already have VS2008 VB Express Edition installed. What am I doing wrong?

T4Toolbox requires Visual Studio 2008 Standard or higher edition. EULA for Express Editions of Visual Studio explicitly prohibits extension by third-party add-ons.

The CodeGenerator item category is only available under Visual c# items.

Can it also be available under VB items?

T4 Toolbox currently supports only C# (as code generation language and target language). This is mainly because I don’t have the extra time that would be required to support Visual Basic. However, there are no technical reasons not to support it. If someone is interested in implementing support for VB in T4 Toolbox, please let me know. I would be happy to help with this.

Alex,

I am not sure how far along you are in your open source project. Why not just use MyGeneration? It is open source and has a CodeSmith converter.

RB

Will do, as soon as I start using NAnt instead of MSBuild…

Hi Oleg!

My Generated SQL file contents is

create procedure Products_Delete
as
delete from Products
where

What is wrong?

It looks like the Products table doesn’t have a primary key.

@hoyoch
You might not have the Northwind database installed.

Oleg,
I have VSTS 2008 and installed the new T4Toolbox.msi. After I add the File template I immediately get the error: ‘Failed to load Assembly ‘C:\Program Files\T4 Toolbox\Bin\T4Toolbox.dll’ for registered directive processor ‘T4Toolbox.DteProcessor”
What is wrong?

Przemek

Thanks for reporting it. There was a problem in build 9.3.21.1. Get build 9.3.21.2 here.

@hoyoch,

I was running into the same problem. My results came back the same way.

create procedure Products_Delete
as
delete from Products
where

No keys, no nothing.

I was running SQL Server 2005 EXPRESS and I recently upgraded to SQL Server Developer.

Solution: I uninstalled SQL Server 2005 Express.

Since this requires the Microsoft SQL Server SMO, SQL Server Express doesn’t have it. One downside to having SQL Server Express.

Now everything’s running just fine.

JD

Great catch, Jonathan. Thanks for sharing this tip. I wonder if installing SMO separately would help as well.

[…] the msdn site but to actually get productive with T4 and to see some sample code in action visit this post on Oleg Sych’s blog. Also go through the various articles on his blog, he’s done some pretty […]

[…] Oleg Sych’s blog and especially T4 Tutorial: Creating your First Code Generator […]

With Visual Studio Team System SP1,
I had to add an assembly directive for Microsoft.SqlServer.Management.SDK.Sfc

Else I would get a Compiling Transformation exception.

Other than that, great post. (and great posts in general, this is a great blog for T4 information)