Поделиться через


Design-Time Code Generation by using T4 Text Templates

Design-time T4 text templates let you to generate program code and other files in your Visual Studio project. Typically, you write the templates so that they vary the code that they generate according to data from a model. A model is a file or database that contains key information about your application’s requirements.

For example, you could have a model that defines a workflow, either as a table or a diagram. From the model, you can generate the software that executes the workflow. When your users’ requirements change, it is easy to discuss the new workflow with the users. Regenerating the code from the workflow is more reliable than updating the code by hand.

Note

A model is a data source that describes a particular aspect of an application. It can be any form, in any kind of file or database. It does not have to be in any particular form, such as a UML model or Domain-Specific Language model. Typical models are in the form of tables or XML files.

You are probably already familiar with code generation. When you define resources in a .resx file in your Visual Studio solution, a set of classes and methods is generated automatically. The resources file makes it much easier and more reliable to edit the resources than it would be if you had to edit the classes and methods. With text templates, you can generate code in the same manner from a source of your own design.

A text template contains a mixture of the text that you want to generate, and program code that generates variable parts of the text. The program code and allows you to repeat or conditionally omit parts of the generated text. The generated text can itself be program code that will form part of your application.

Creating a Design-Time T4 Text Template

To create a design-time T4 template in Visual Studio

  1. Create a Visual Studio project, or open an existing one.

    For example, on the File menu, point to New, and then click Project.

  2. Add a text template file to your project and name it NewTemplate.tt.

    To do this, in Solution Explorer, right-click your project and point to Add, and then click New Item. In the Add New Item dialog box select Text Template from the Templates pane. Name the file NewTemplate.tt.

    Notice that the Custom Tool property of the file is TextTemplatingFileGenerator.

  3. Open the file. It will already contain the following directives:

    <#@ template debug="false" hostspecific="false" language="C#" #>
    <#@ output extension=".txt" #>
    

    If you added the template to a Visual Basic project, the language attribute will be "VB".

  4. Add some text at the end of the file. For example:

    Hello, world!
    
  5. Save the file.

    You might see a Security Warning message box that asks you to confirm that you want to run the template. Click OK.

  6. In Solution Explorer, expand the NewTemplate.tt node and you will find a file that is named NewTemplate.txt. The file contains the text that you entered.

    Note

    If your project is a Visual Basic project, you must click Show All Files in order to see the NewTemplate.txt output file.

Regenerating the code

A template will be executed, generating the subsidiary file, in any of the following cases:

  • Edit a template and then change focus to a different Visual Studio window.

  • Save the template.

  • Click Transform All Templates in the Solution Explorer toolbar. This will transform all the templates in the Visual Studio solution.

  • Right-click one or more files in Solution Explorer and then click Run Custom Tool. Use this method to transform a selected set of templates.

You can also set up a Visual Studio project so that the templates are executed when the data files that they read have changed. For more information, see Regenerating the Code Automatically.

Generating Variable Text

Text templates let you use program code to vary the content of the generated file.

To generate text by using program code

  1. Change the content of the .tt file:

    <#@ template debug="false" hostspecific="false" language="C#" #>
    <#@ output extension=".txt" #>
    <#int top = 10;
    
    for (int i = 0; i<=top; i++) { #>
    The square of <#= i #> is <#= i*i #>
    <# } #>
    
    <#@ template debug="false" hostspecific="false" language="VB" #>
    <#@ output extension=".txt" #>
    <#Dim top As Integer = 10
    
    For i As Integer = 0 To top
    #>
        The square of <#= i #> is <#= i*i #>
    <#
    Next
    #>
    
  2. Save the .tt file, and inspect the generated .txt file again. It lists the squares of the numbers from 0 to 9.

Notice that statements are enclosed within <#...#>, and single expressions within <#=...#>. For more information, see Writing a T4 Text Template.

If you write the generating code in Visual Basic, the template directive should contain language="VB". "C#" is the default.

Generating Code or Resources for Your Solution

You can generate program files that vary, depending on a model. A model is an input such as a database, configuration file, UML model, DSL model, or other source. You usually generate several program files are from the same model. To achieve this, you create a template file for each generated program file, and have all the templates read the same model.

To generate program code or resources

  1. Change the output directive to generate a file of the appropriate type, such as .cs, .vb, .resx, or .xml.

  2. Insert code that will generate the solution code that you require. For example, if you want to generate three integer field declarations in a class:

    <#@ template debug="false" hostspecific="false" language="C#" #>
    <#@ output extension=".cs" #>
    <# var properties = new string [] {"P1", "P2", "P3"}; #>
    class MyGeneratedClass {
    <# 
      foreach (string propertyName in properties)
      { #>
      private int <#= propertyName #> = 0;
    <# } #>
    }
    
    <#@ template debug="false" hostspecific="false" language="VB" #>
    <#@ output extension=".cs" #>
    <# Dim properties = {"P1", "P2", "P3"} #>
    class MyGeneratedClass {
    <# 
       For Each propertyName As String In properties
    #>
      private int <#= propertyName #> = 0;
    <#
       Next
    #>
    }
    
  3. Save the file and inspect the generated file, which now contains the following code:

    class MyGeneratedClass {
      private int P1 = 0; 
      private int P2 = 0;
      private int P3 = 0;
    }
    

Generating Code and Generated Text

When you generate program code, it is most important to avoid confusing the generating code that executes in your template, and the resulting generated code that becomes part of your solution. The two languages do not have to be the same.

The previous example has two versions. In one version, the generating code is in C#. In the other version, the generating code is Visual Basic. But the text generated by both of them is the same, and it is a C# class.

In the same way, you could use a Visual C# template to generate code in any language. The generated text does not have to be in any particular language, and it does not have to be program code.

Structuring text templates

As a matter of good practice, we tend to separate the template code into two parts:

  • A configuration or data-gathering part, which sets values in variables, but does not contain text blocks. In the previous example, this part is the initialization of properties.

    This is sometimes called the "model" section, because it constructs an in-store model, and typically reads a model file.

  • The text-generation part (foreach(...){...} in the example), which uses the values of the variables.

This is not a necessary separation, but it is a style which makes it easier to read the template by reducing the complexity of the part that includes text.

Reading files or other sources

To access a model file or database, your template code can use assemblies such as System.XML. To gain access to these assemblies, you must insert directives such as these:

<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.IO" #>

The assembly directive makes the specified assembly available to your template code, in the same manner as the References section of a Visual Studio project. You do not need to include a reference to System.dll, which is referenced automatically. The import directive lets you use types without using their fully qualified names, in the same manner as the using directive in an ordinary program file.

For example, after importing System.IO, you could write:

<# var properties = File.ReadLines("C:\\propertyList.txt");#>
...
<# foreach (string propertyName in properties) { #>
...
<# For Each propertyName As String In 
             File.ReadLines("C:\\propertyList.txt")
#>

Opening a file with a relative pathname

To load a file from a location relative to the text template, you can use this.Host.ResolvePath(). To use this.Host, you must set hostspecific="true" in the template:

<#@ template debug="false" hostspecific="true" language="C#" #>

Then you can write, for example:

<# string fileName = this.Host.ResolvePath("filename.txt");
  string [] properties = File.ReadLines(filename);
#>
...
<#  foreach (string propertyName in properties { #>
...
<# Dim fileName = Me.Host.ResolvePath("propertyList.txt")
   Dim properties = File.ReadLines(filename)
#>
...
<#   For Each propertyName As String In properties
...
#>

You can also use this.Host.TemplateFile, which identifies the name of the current template file.

The type of this.Host (in VB, Me.Host) is Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost.

Getting data from Visual Studio

To use services provided in Visual Studio, by set the hostSpecific attribute and load the EnvDTE assembly. For example:

<#@ template hostspecific="true" language="C#" #>
<#@ output extension=".txt" #>
<#@ assembly name="EnvDTE" #>
<#
  IServiceProvider serviceProvider = (IServiceProvider)this.Host;
  EnvDTE.DTE dte = (EnvDTE.DTE) serviceProvider.GetService(typeof(EnvDTE.DTE));
#>

Number of projects in this VS solution:  <#= dte.Solution.Projects.Count #>

Regenerating the code automatically

Typically, several files in a Visual Studio solution are generated with one input model. Each file is generated from its own template, but the templates all refer to the same model.

If the source model changes, you should re-run all the templates in the solution. To do this manually, click Transform All Templates in the toolbar of Solution Explorer.

If you have installed Visual Studio Visualization and Modeling SDK, you can have all the templates transformed automatically whenever you perform a build. To do this, edit your project file (.csproj or .vbproj) in a text editor and add the following lines near the end of the file, after any other <import> statements:

<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\TextTemplating\v10.0\Microsoft.TextTemplating.targets" />
<PropertyGroup>
   <TransformOnBuild>true</TransformOnBuild>
   <!-- Other properties can be inserted here -->
</PropertyGroup>

For more information, see Code Generation in a Build Process.

Error reporting

To place error and warning messages in the Visual Studio error window, you can use these methods:

Error("An error message");
Warning("A warning message");

Converting an existing file to a template

A useful feature of templates is that they look very much like the files that they generate, together with some inserted program code. This suggests a useful method of creating a template. First create an ordinary file as a prototype, such as a Visual C# file, and then gradually introduce generation code that varies the resulting file.

To convert an existing file to a design-time template

  1. To your Visual Studio project, add a file of the type that you want to generate, such as a .cs, .vb, or .resx file.

  2. Test the new file to make sure that it works.

  3. In Solution Explorer, change the file name extension to .tt.

  4. Verify the following properties of the .tt file:

    Custom Tool =

    TextTemplatingFileGenerator

    Build Action =

    None

  5. Insert the following lines at the beginning of the file:

    <#@ template debug="false" hostspecific="false" language="C#" #>
    <#@ output extension=".cs" #>
    

    If you want to write the generating code of your template in Visual Basic, set the language attribute to "VB" instead of "C#".

    Set the extension attribute to the file name extension for the type of file that you want to generate, for example .cs, .resx, or .xml.

  6. Save the file.

    A subsidiary file is created, with the specified extension. Its properties are correct for the type of file. For example, the Build Action property of a .cs file would be Compile.

    Verify that the generated file contains the same content as the original file.

  7. Identify a part of the file that you want to vary. For example, a part that appears only under certain conditions, or a part that is repeated, or where the specific values vary. Insert generating code. Save the file and verify that the subsidiary file is correctly generated. Repeat this step.

Guidelines for Code Generation

Please see Guidelines for Writing T4 Text Templates.

Next steps

Next step

Topic

Write and debug a more advanced text template, with code that uses auxiliary functions, included files, and persistent data.

Writing a T4 Text Template

Generate documents from templates at run time.

Run-Time Text Generation by using Preprocessed T4 Text Templates

Run text generation outside Visual Studio.

Generating Files with the TextTransform Utility

Transform your data in the form of a domain-specific language.

Generating Code from a Domain-Specific Language

Write directive processors to transform your own data sources.

Customizing T4 Text Transformation

See Also

Concepts

Guidelines for Writing T4 Text Templates