T4 Code Generator Extensibility

In my previous post, I covered the overall structure of the T4 Code Generator and how it can be used instead of the default code generator. Here I will cover how you can customize it.

The T4 Code Generator is intended to be extensible.  You should be able to derive from it and tweak the code generation to modify the generated code as required. For that, the generator has the following extensibility points:

1.

Each of the 5 code generators (CSharpDomainContextGenerator, CSharpEntityGenerator, CSharpComplexObjectGenerator, CSharpEnumGenerator and CSharpWebContextGenerator) contain a number of protected virtual methods. Each of these methods roughly map to the corresponding part of generated code. To modify the code for a particular component, you can derive from that particular code generator and override the method for the part for which you want to modify the generated code.

For example, consider the CSharpDomainContextGenerator that generates DomainContext code in C# given a DomainServiceDescription. It contains the following protected virtual methods:

protected virtual void GenerateAttributes(IEnumerable<Attribute> attributes);
protected virtual void GenerateBody();
protected virtual void GenerateClassDeclaration();
protected virtual void GenerateConstructors();
protected virtual void GenerateCustomMethods();
protected virtual void GenerateDomainOperationEntry(DomainOperationEntry domainMethod);
protected virtual void GenerateEntityContainer();
protected virtual void GenerateEntitySet(Type entityType);
protected virtual void GenerateEntitySets();
protected virtual void GenerateExtensibilityMethods();
protected virtual void GenerateInvokeOperations();
protected virtual void GenerateQueryMethod(DomainOperationEntry domainOperationEntry);
protected virtual void GenerateQueryMethods();

Now, if you want to modify the code generated for the DomainContext constructors, you would declare a type that derives from the CSharpDomainContextGenerator and override the GenerateConstructors() method. Note that if you want to add code before or after the code generated by the base method, you can follow the following pattern (which is pretty convenient in quite a few cases):

class MyDomainContextGenerator : CSharpDomainContextGenerator
{
protected override void GenerateConstructors()
{
//Add code before

base.GenerateConstructors();

//Add code after
}
}

Of course, you can completely change the generate code by not calling into the base class implementation, if that suits your needs.

Each of the above virtual methods generate code as indicated by their names. All the other code generators also have such virtual methods that can be overridden in a similar fashion. I won’t go into the details of each one of those here.
The GenerateBody() method is special (it exists on all the code generators). This method generates the entire body of the class by calling into other protected virtual methods above. If you want to add additional code to the class, you will override this method, call base.GenerateBody() and then add any additional code that you might want to generate.
Note: This method does not include the class declaration (it does not call the ClassDeclaration() method).
2.

The ClientCodeGenerator class (which is the base class for CSharpClientCodeGenerator) contains protected abstract properties for the 5 code generators.

protected abstract ComplexObjectGenerator ComplexObjectGenerator { get; }
protected abstract DomainContextGenerator DomainContextGenerator { get; }
protected abstract EntityGenerator EntityGenerator { get; }
protected abstract EnumGenerator EnumGenerator { get; }
protected abstract WebContextGenerator WebContextGenerator { get; }

The CSharpClientCodeGenerator populates them with the default implementations of the code generators above. To make the code generator use your custom generator for any of the above generators, define a type that derives from CSharpClientCodeGenerator and override the property for the generator you want to substitute with your own. This now becomes your custom T4 Code Generator. So the WCF RIA Services code generation process will use this type to do all code generation. For that you will have to add the attribute [DomainServiceClientCodeGenerator] on the type you define. For example, to use the custom DomainContextGenerator from the above example, you will define a type as follows:

[DomainServiceClientCodeGenerator(“MyCustomGenerator”, “C#”)]
class MyCSharpClientCodeGenerator : CSharpClientCodeGenerator
{
protected override DomainContextGenerator DomainContextGenerator
{
get
{
return new MyDomainContextGenerator();
}
}
}

Now this is your T4 Code Generator. Then when you add a reference to your assembly (containing these types) on the server and a <RiaClientCodeGeneratorName>property indicating the assembly name to the Silverlight project file, you will get your custom code generated on the client.

For a complete walkthrough of the T4 Code Generation customization process (along with ways to work around the bugs in it), please refer to a great post by Jeff here.

T4 Code Generator for WCF RIA Services

I had always wanted to have a technical blog since I joined Microsoft, but somehow never ended up writing one. Now, finally, after we released WCF RIA Services V1.0 SP1 Beta and WCF RIA Services Toolkit October 2010 last week, I have decided to blog about the T4 Code Generation feature we have added to the WCF RIA Services Toolkit.
This post will focus on the design and use of the feature. I will follow-up with a post on the extensibility mechanism of the code generator.

Overview:
The Code Generation module that ships with RIA Services is now extensible to allow different Code Generators to be used instead of the default CodeDom based code generation. This is to allow customers to modify the code generation process and generate custom code on the client.

To that end we have shipped the T4 Code Generator that is customizable to allow you to tweak the code generation and modify the generated code as required. Currently it supports only C# based code generation.

Structure:

The T4 Code Generator consists of 2 parts:
1. CSharpClientCodeGenerator

This is the main component of the T4 Code Generation process that hooks into the extensibility features of the WCF RIA Services Code generation process. It controls code generation of different types on the client.

2. The 5 different code generators:

  • CSharpDomainContextGenerator
  • CSharpEntityGenerator
  • CSharpComplexObjectGenerator
  • CSharpEnumGenerator
  • CSharpWebContextGenerator

As their names indicate, each of these generators generate code for their respective types (CSharpEntityGenerator generates Entity types in C# and so on). These code generators generate code using the TextTemplates. (For more information about T4 you can follow the msdn documentation or Oleg Synch’g blog)

The CSharpClientCodeGenerator implements the IDomainServiceClientCodeGenerator interface and returns a string containing the generated code. It also declares itself to the WCF RIA Services code generation using the [DomainServiceClientCodeGenerator] attribute (which uses a MEF based approach underneath).

The code generation process calls passes a list of DomainServiceDescriptions to the CSharpClientCodeGenerator. Using that list, it generates code for DomainContexts, Entities, ComplexObjects, Enums and the application wide WebContext by calling into the respective individual code generators.

The CSharpClientCodeGenrator also contains public properties for ClientCodeGenerationOptions and ICodeGenerationHost. It acts as the context for each of the different code generators.

Using the T4 Code Generator

To use the T4 Code Generator instead of the default CodeDom based code generator you will have to do the following:

  1. Add a reference to the assembly Microsoft.ServiceModel.DomainServices.Tools.TextTemplate which contains the T4 Code Generator.
  2. Edit the silverlight project file and add the following tag:
    <RiaClientCodeGeneratorName>
    Microsoft.ServiceModel.DomainServices.Tools.TextTemplate.CSharpGenerators.CSharpClientCodeGenerator,
    Microsoft.ServiceModel.DomainServices.Tools.TextTemplate,
    Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
    </RiaClientCodeGeneratorName>

Now when you build, the code will be generated on the client using this code generator.

I will cover how this T4 Code Generators can be extended to generate custom code in my next post.