T4 and CodeDOM - Better Together
T4 and CodeDOM are both code generation tools. T4 generates code from a textual template, in essence, using string concatenation. CodeDOM generates code from an object graph where each element represents a common language construct - an if statement, a variable assignment, etc. T4 allows you to put a lot of the output code in template blocks, making the code generator easy to create and customize, but forcing you to create a separate code generator for each output language you need to support. CodeDOM generators are a often verbose and difficult to read, but allow you to generate multiple output languages from a single code generator.
Simplicity and ease of use makes T4 a better code generation tool for application developers, who typically don’t care about generating code in different languages and don’t have time to do it. On the other hand, ability to support multiple output languages makes CodeDOM a better code generation platform for tool developers, who have to support multiple output languages and don’t want to maintain several different code generators that generate the same thing in different languages.
During the past year, we have seen T4 getting adopted by an increasing number of tool developers inside Microsoft, most importantly by the ASP.NET MVC and ADO.NET EF teams. This is great news for application developers, who can now generate their code their way. Or is it? What about tool developers who now have to supply two code generators (T4/C# and T4/VB) instead of one? Actually, make that three code generators - they still have to supply and maintain the original CodeDOM-based generators to support other .NET languages. It is only a matter of time before this added complexity will show its ugly face in the form of quality problems, missing features, missing output languages, etc. In the end, the application developers might just get what they wished for - completely custom code generators that are their to implement and maintain.
Consider the following template from the previous article - Generating Code from DSL Models. This template (ClassTemplate) generates a class declaration based on a definition in a Class Diagram DSL (ModelClass). If you are using T4 Toolbox, you have already recognized the base class, Template, which inherits from TextTranformation. We could have also used pure T4 and have ClassTemplate inherit from TextTransformation directly.
C#
<#+ public class ClassTemplate : Template { public ModelClass ModelClass { get; set; } public override string TransformText() { #> namespace <#= TransformationContext.DefaultNamespace #> { using System; using System.Collections.Generic; public class <#= this.ModelClass.Name #> { <#+ foreach (ModelAttribute attribute in this.ModelClass.Attributes) { #> public <#= attribute.Type #> <#= attribute.Name #> { get; set; } <#+ } #> } } <#+ return this.GenerationEnvironment.ToString(); } } #>
Visual Basic
<#+ Public Class ClassTemplate Inherits Template Public ModelClass As ModelClass Public Overrides Function TransformText() #> Imports System Imports System.Collections.Generic Namespace <#= TransformationContext.DefaultNamespace #> Public Class <#= Me.ModelClass.Name #> <#+ For Each attribute As ModelAttribute in Me.ModelClass.Attributes #> Public <#= attribute.Name #> As <#= attribute.Type #> <#+ Next #> End Class End Namespace <#+ Return Me.GenerationEnvironment.ToString() End Function End Class #>
Keep in mind that this template is simple only because it’s a sample. A real-world code generator that provides actual value, such as the LINQ to SQL entity class generator, can easily be thousands lines long. As a tool developer on the T4 Toolbox team, I don’t want to translate the LINQ to SQL template from C# to Visual Basic only so that I have to then maintain both. I would prefer to have a single code base to develop and maintain, but as a tool developer, I feel that I should not limit the audience of my tool just to C# developers.
Do we have to choose between the needs of application developers and the needs of tool developers? Is it possible for tool developers to use CodeDOM to develop code generators and for application developers to use T4 to customize and extend them? Well… YES! Thanks for asking!
How Would That Work?
A Tool developer would implement the ClassTemplate using CodeDOM and encapsulate code that generates individual type members, such as fields or properties, in virtual methods, such as RenderProperty.
C#
public class ClassTemplate : Template { // … protected virtual void RenderProperty(ModelAttribute attribute) { CodeMemberProperty property = new CodeMemberProperty(); property.Attributes = MemberAttributes.Public | MemberAttributes.Final; property.Type = new CodeTypeReference(attribute.Type); property.Name = this.LanguageProvider.CreateEscapedIdentifier(attribute.Name); property.GetStatements.Add( new CodeMethodReturnStatement( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), this.FieldName(attribute.Name)))); property.SetStatements.Add( new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), this.FieldName(attribute.Name)), new CodePropertySetValueReferenceExpression())); this.LanguageProvider.GenerateCodeFromMember( property, this.generationWriter, null); } // … }
There is no Visual Basic example of this code, because in this case, the tool developer chose to use C# as the programming language.
An Application developer would override the virtual method in a T4 template and use text templating instead of CodeDOM. The code below illustrates how the code generating properties could be extended to generate DataMemberAttribute declarations and make the properties serializable by WCF.
C#
<#+ class T4ClassTemplate : ClassTemplate { protected override void RenderProperty(ModelAttribute attribute) { #> [DataMember] <#+ base.RenderProperty(attribute); } } #>
Visual Basic
<#+ Class T4ClassTemplate Inherits ClassTemplate Protected Overrides Sub RenderProperty(attribute As ModelAttribute) #> <DataMember> _ <#+ MyBase.RenderProperty(attribute) End Sub End Class #>
Note that the application developer is using T4 text templating features to extend a code generator written with CodeDOM. She did not have to reimplement the entire code generator in T4, only modify the logic she was interested in changing. In this example, she has extended the logic that generates property code using her language of choice - either Visual Basic or C#.
What’s the Trick?
Notice that our RenderProperty method uses GenerateCodeFromMember method to convert property declaration from a CodeMemberProperty object to text in the GenerationEnvironment, a StringBuilder object that accumulates output generated by our Template. This is why we can override the RenderProperty method in T4 and use text templating to change the generated code.
The rest of the magic is happening in the TransformText method, which builds the CodeDOM graph of the class we are generating using CodeSnippetTypeMember objects instead of the CodeMemberProperty, CodeMemberField and CodeMemberMethod objects we would use normally. The text for the CodeSnippetTypeMember objects is extracted from the GenerationEnvironment.
public class ClassTemplate : Template { public CodeDomProvider LanguageProvider = CodeDomProvider.CreateProvider("CSharp"); private StringWriter generationWriter; private CodeGeneratorOptions options; public ModelClass ModelClass { get; set; } public ClassTemplate() { this.generationWriter = new StringWriter(this.GenerationEnvironment); } public override string TransformText() { CodeNamespace @namespace = new CodeNamespace(TransformationContext.DefaultNamespace); @namespace.Imports.Add(new CodeNamespaceImport("System")); @namespace.Imports.Add(new CodeNamespaceImport("System.Collections.Generic")); CodeTypeDeclaration @class = new CodeTypeDeclaration(this.ModelClass.Name); @class.TypeAttributes = TypeAttributes.Public; @namespace.Types.Add(@class); foreach (ModelAttribute attribute in this.ModelClass.Attributes) { this.RenderField(attribute); @class.Members.Add(new CodeSnippetTypeMember(this.GenerationEnvironment.ToString())); this.GenerationEnvironment.Length = 0; } foreach (ModelAttribute attribute in this.ModelClass.Attributes) { this.RenderProperty(attribute); @class.Members.Add(new CodeSnippetTypeMember(this.GenerationEnvironment.ToString())); this.GenerationEnvironment.Length = 0; } this.LanguageProvider.GenerateCodeFromNamespace(@namespace, this.generationWriter, null); return this.GenerationEnvironment.ToString(); } }
In other words, to generate a particular type member, such as property, we a) build it as a CodeDOM graph; b) convert it to text stored in GenerationEnvironment of the Template; c) extract text from GenerationEnvironment and wrap it in a CodeSnippetTypeMember object; and d) finish building the CodeDOM graph of the type using code snippet objects. Because steps a) and b) occur in a virtual method, we can override it using pure text templating functionality of T4.
What’s the Catch?
Unfortunately, CodeDOM does not provide code snippet descendants for all types. In particular, there is no CodeSnippetTypeDeclaration descendant for CodeTypeDeclaration. Therefore, it is not possible to extract generation of the class declaration from the TransformText method into a virtual method similar to RenderProperty that it could be overridden using T4. Specifically, it would not be possible to extend the code generator in this example and use T4 text templating to generate DataContractAttribute declaration for the class. We can provide an event or a virtual method in ClassTemplate that would allow T4ClassTemplate to access and modify the CodeTypeDeclaration before the output code is generated from it, however T4ClassTemplate would need to manipulate it in the CodeDOM form. It is very much possible to do in T4 code, but would not be as easy as using the text templating.
So What Are You Saying?
As long as a CodeDOM-based generator provides adequate extensibility points, such as virtual methods and/or events, the application developers can extend or replace parts of its logic in T4 without having to reimplement or maintain the entire generator. Although this approach is significantly more complex than creating a T4-based code generator for a single output language, it is comparable in size and complexity to having multiple T4-based generators, producing the same code in different output languages. As the size and complexity of the generated code grows, maintaining several T4-based generators in different .NET languages becomes more complex than maintaining a single CodeDOM-based generator. This approach is a compromise between the needs of application developers, who want to customize generated code using T4 and their language of choice, and tool developers, who want to maintain a single code base.
Download
Source code, C# and Visual Basic. Follow instructions in the previous article to compile and run it.



Howdy. Check out this project:
http://metasharp.codeplex.com
It’s specifically designed to solve this problem. There are multiple types of transformations supported, Text->AST, AST->AST, AST->Text are just some common scenarios. Specifically, in this case, you generate code in a T4 style MetaSharp templating language which compiles to CodeDom…
Additionally, you could get the same results by creating a C# T4 template then using NRefactory to parse the C# and convert it into CodeDom objects so it can compile in VB projects.
MetaSharp does the same thing essentially, except the language is intentionally part way between C# and VB and is just slightly more complex than bare bones CLS compliance. Also, you can use it to compile to dynamic code or anything else.
But I agree with your line of thinking for the most part, for what it’s worth.