Last time, in Part II, I described a highly structured approach to creating and harnessing a preprocessed template to generate a simple C# class from metadata. I put lots of nice extensibility points in that template and now we’re going to exploit them to show how easy it gets to be to customize a template once you have such a structure in place.

Here’s an edited reminder of the code we wanted to generate:

   1: /// <summary>
   2: /// A class to carry data about a book in a library system.
   3: /// </summary>
   4: /// <remarks>
   5: /// This code generated by the DataClass.tt template - DO NOT MODIFY THIS CODE.
   6: /// </remarks>
   7: [Serializable]
   8: internal partial class Book
   9: {
  10:     /// <summary>
  11:     /// The title of the book.
  12:     /// </summary>
  13:     /// <remarks>
  14:     /// This code generated by the DataClass.tt template - DO NOT MODIFY THIS CODE.
  15:     /// </remarks>
  16:     public string Title { get; set; }
  17:  
  18:   … More methods elided …
  41:  
  42: }

We’d like all of the summary comments to contain a reminder not to edit generated code and we need our class to be marked as internal and Serializable.

If we take a look at the customization points in our base template, DataClass.tt, we’ll see we have what we need. We can override Summary() and ClassHeader() to add the extra text we need:

   1: <#+
   2: // ---------------------------------------------
   3: // Override some of the snippet methods from DataClass.tt to change the template's behavior.
   4: // ---------------------------------------------
   5:  
   6: /// <summary>
   7: /// Override the summary method to remind readers more regularly that this code is auto-generated by a tool
   8: /// </summary>
   9: public  override void Summary(string comment)
  10: {
  11:     base.Summary(comment);
  12: #>
  13: /// <remarks>
  14: /// This code generated by the DataClass.tt template - DO NOT MODIFY THIS CODE.
  15: /// </remarks>
  16: <#+
  17: }
  18:  
  19: /// <summary>
  20: /// Override the class header method to make the generated class partial, internal and Serializable
  21: /// </summary>
  22: public  override void ClassHeader(TypeDescription type)
  23: {
  24: #>
  25: [Serializable]
  26: internal partial class <#= type.Name #>
  27: <#+ 
  28: }
  29: #>
Note that Summary() ‘s override is purely additive, as it calls the base implementation to get the regular summary before adding the extra comment.  By contrast, the override of ClassHeader()redefines the output snippet.  It does this as the accessibility of the type isn’t independently customizable without replacing the whole declaration line.  If you keep your output snippets down to a few lines of code at a time, then this kind of thing generally doesn’t compromise maintainability, and means that you don’t need to try and anticipate every last customization need in advance.
 
The next question is, where to put these overrides?  The virtual methods are defined in DataClass.tt and then individual template classes derived from that are created in Book.tt and Author.tt where the specific metadata is defined.  As these would be project-wide customizations, I put them in a separate file, ProjectSpecific.t4.  To activate customization, it’s then a simple matter of including that file with a single line in each template class definition.  In the sample I posted as part of Part II, I only included it in the Author.tt file, not Book.tt in order to demonstrate the difference in generated code with and without it.  An alternative would have been to put the overrides in an intermediate base class, but that seemed overkill for this sample – either way, there is only a single source for the code.
 
To see the difference with and without, first you regenerate the Templates project, to take advantage of template changes, then build that project to compile the precompiled templates.   Finally regenerate the simple harness templates in the TemplateUse project.  You could use the T4/msbuild integration from the Visualization and Modeling SDK to automate this process.  To be clear, you don’t need to separate the Templates and BaseTemplates projects; I just wanted to illustrate the idea of a central repository of templates being customized on a per-project basis.
 
We’ve now seen how to use the new assembly non-locking and T4 preprocessed template inheritance features in Visual Studio 2010 SP1 together to set out a clean, structured approach to template reuse and extensibility for medium-scale code generation projects.
 
Can we go further and apply the same techniques to regular T4 templates? I’ll look at that next time
 
Technorati Tags: ,