Text transformations with the Custom Templating Library

The CustomTemplating library is used to generate code and other text artifacts based on templates. These text transformations are based on T4 (Text Template Transformation Toolkit) which is part Visual Studio 2008 (or higher). While T4 is mainly used inside the Visual Studio IDE to generate (code) files inside the open solution, this library enables the usage in custom developed applications, for example VS Add-ins, without the need for Visual Studio IDE as T4 Host.

It simplifies the start of the text transformation process and allow to pass arguments from the application to the template transformation. More information about template used by T4 can be found in one of my blog posts http://www.timcools.net/post/2009/03/09/Template-based-code-generation-with-T4.aspx and on the MSDN site http://msdn.microsoft.com/en-us/library/bb126445.aspx.

Due to that fact that T4 is part of the Visual Studio, using the CustomTemplating Library requires that Visual Studio is installed.

Simple transformations

To start the transformations the Template class is used. This static class will take the template and the arguments as input and returns the result of the transformation as string. All transformations will happen in the current AppDomain.

Transformations.png

Template contains 3 transform methods with different overloads that allows to pass arguments to the text transformation process.

public string Transform(string template) 
public string Transform(string template, string argumentName, object argumentValue) 
public string Transform(string template, TemplateArgumentCollection arguments) 

The first argument is the template as string. This template can come from different sources, for example from an external template file, from an embedded resource or from the input of the user of your application.

The first overload only accepts the template without any argument. Following example show a template without arguments.

This is a template displaying the current date & time: <#= System.DateTime.Now #>

//Start transformation without argument
string output = Template.Transform(template);

It will return the transformed template as string. In this example the output will be "This is a template displaying the current date & time: 07/24/2009 12:34:56"

The second overload allows to pass one argument to the transformation. The second parameter of the method specifies the name of the argument. This name can be used in the transformation to access the value of the argument just like it was a property. The third parameter specifies the value of the argument.

Following template example uses one argument called 'Name' that is passed to the transformation.

<#@ property name="Name" type="System.String" processor=""PropertyProcessor" #>
This is a template displaying one argument: <#= Name #>

//Start transformation with a single argument
string output2 = Template.Transform(template2, "Name", "Homer Simpson");

It will return the transformed template as string, taking in account the argument passed to the method. In this example the output will be: "This is a template displaying one argument: Homer Simpson";

The third overload allows to pass a collection of arguments to the transformation. Each argument has its own name used to access the value of the argument.

When a template is transformed with three arguments like following example, all arguments are passed to the transformation in one collection.

<#@ property name="FirstName" type="System.String" processor=""PropertyProcessor" #>
<#@ property name="LastName" type="System.String" processor=""PropertyProcessor" #>
<#@ property name="Color" type="System.Drawing.Color" processor=""ProperyProcessor" #>
This is a template displaying multiple arguments: 
<#= FirstName #> <#= LastName #> is <#= Color.Name #>

//Start transformation with multiple arguments
var arguments = new TemplateArgumentCollection
{    
   //Argument             Name     &  Value
   new TemplateArgument("FirstName", "Homer"),
   new TemplateArgument("LastName", "Simpson"),
   new TemplateArgument("Color", Color.Yellow)
};
string output3 = Template.Transform(template3, arguments);

It will return the transformed template as string, taking in account all arguments passed to the method. In this example the output will be: "This is a template displaying multiple arguments: Homer Simpson is Yellow";

TextTransformer and DomainTextTransformer

When more control is needed over the transformations the TextTransformer and DomainTextTransformer classes are used. Both classes provide the same 3 overloads as the Template class, but they will behave slightly different. TextTransformer will transform in the current AppDomain, and the DomainTextTransformer will transform in a newly created AppDomain. Both classes implement the ITextTransformer interface.

To compile a template in the current AppDomain we should instantiate the TextTransformer class. Then we call one of the Transform method overloads of the object.

string template = "This is a template displaying the current date & time: <#= System.DateTime.Now #>";
var transformer = new Transformer();

//Start transformation without argument
string output = transformer.Transform(template);

To compile templates in a separated App-Domain, you create an instance of the DomainTextTransformer object. This object provides the same method overloads as the TextTransformer object. To unload the compiled templates from the memory the Dispose method is called.

string template = "This is a template displaying the current date & time: <#= System.DateTime.Now #>";
using (DomainTextTransformer transformer = new DomainTextTransformer())
{
    //Start transformation without argument
    string output = transformer.Transform(template);

    //Dispose is called implicitly and all 
    //compiled templates are unloaded here
}  

When the DomainTextTransformer is used the memory will grow each time a different template is transformed. The AutoRecycle can prevent this by unloading and recreating the App-Domain wherein the transformations happen. This feature is called App-Domain recycling and is trigged by calling the Recycle method.

domain.Recycle();   //App-Domain is unloaded en recreated here 

It is also possible to recycle the App-Domain automatically after a predefined number of transformations. To enable this feature the AutoRecycle property is set to true. The number of transformations that occurs before the App-Domain is recycled is 25 by default, but it is configurable by setting the RecycleThreshold property.

var domain = new DomainTextTransformer();
domain.AutoRecycle = true;
domain.RecycleThreshold = 5; //App-Domain is recycled after 5 transformations

Both TextTransformer and DomainTextTransformer support the Path property. This property allows specifying an additional path wherein the host will look when resolving file paths or for templates that needs to be included. Beside this path the host will look in the application folder to resolve file paths.

The TextTransformer objects provides an event named ClassDefinitionGenerated which is raised when a class definition is generated during the transformation process. It can be used for debugging and logging purposes.

void SomeMethod()
{
  //Allows us to show the generated class
  TextTransformer.ClassDefinitionGenerated += ClassDefinitionGenerated;
}

private void ClassDefinitionGenerated(
    object sender, ClassDefinitionEventArgs e)
{          
  Console.WriteLine(e.ClassDefinition);         
}


Because the requesting application and the transformations are running in a different App-Domain, all the objects passed from the application to the transformation class need to be serialized. This serialization uses the binary formatter. This implies that the classes of the objects should be decorated with the Serializable attribute, implement the ISerializable interface or should derive from MarshalByRefObject. Otherwise a SerializationException is thrown.

[Serializable]
public class Property
{
    …
}

Restrictions

Text transformations are not possible in a partially trusted environment. This is not a restriction of the CustomTemplating library, but of the T4 library.

When objects are passed to a transformation that occurs in a separated AppDomain all the objects should be serializable. This is due to the fact that internally .NET remoting is used to communicate with objects within another AppDomain. .NET remoting requires the objects to be serializable by decoration the objects with the Serializable attribute.

License

CustomTemplating Version 1.1 is released under LGPL v3.0. (More info: GNU Website GNU Website http://www.gnu.org/copyleft/lesser.html and wikipedia http://en.wikipedia.org/wiki/LGPL

Last edited Sep 6, 2009 at 1:19 PM by TimCools, version 5