Peter Goodman bio photo

Peter Goodman

A developer living in Auckland building software using all sorts of bits and pieces I find along the way. Originally from Northern Ireland.

Twitter Google+ LinkedIn Github

Cut to the chase

OK. So you just want to know how to do it? Modify your template directives to the following.

<#@ template inherits="Microsoft.VisualStudio.TextTemplating.VSHost.ModelingTextTransformation" language="C#v3.5" #>

What?..Why?

Recently I have been doing a lot of work in Microsoft's Domain Specific Language Tools for Visual Studio. In fact for the past 8 months this has pretty much been my main focus. I've learned a lot of things on the way and it occurred to me that the development of T4 templates used in DSL tools would be made a lot easier with the use of extension methods. To understand why, you need to understand the concepts behind DSL tools and the development process involved in creating a model to define your problem domain and templating to create the desired output.

One of the problems that becomes apparent in the latter stages of developing a DSL tools implementation is that templating can become quite messy. In DSL tools you create a model of the API which will allow you to interact with the visual elements and this generates code containing the classes you defined in the pattern necessary for these visual elements to be able to use them. After you are happy with the model you move on to generating your output. Typically this takes the form of T4 templates which will generate C# classes.

T4 templating, despite the fact that the engine is now built into Visual Studio 2008, does not get any designer support. It is an ASP-like syntax which compiles at template-transform-time into C# class files used to generate the output into files in your solution. Even using editors like Clarius Consulting's T4 Editor does not give you the intellisense you are used to in ordinary C# (well not yet anyway....see here). What tends to happen is that developers make extensions to the model API classes generated by the DSL tools using partial classes to support common scenarios in templating (don't even go down the path of static helper classes.....eek). This seems a good idea at first but after some time your API classes tend to suffer from explosion of methods and properties. These methods and properties are not helpful in defining your domain and do not make sense in Domain Driven Design concepts, they have no place in the ubiquitous language of the domain, they only make sense to the text transformation process employed in your T4 templates.

The solution can be to add common base templates which are included in your task specific templates using an ASP-like include directive. An issue with this is you typically create new class structures to encapsulate the functionality you want to perform. This has an obvious overhead in setup and maintenance, also the code in the include template is compiled into the same class as the task specific template which simply adds to the issues.

No wonder then that I was interested in the possibility of using Extension Methods to extend the DSL generated API with templating specific methods. After asking the question in the VSX forum I submitted a Microsoft Connect issue and got the answer of the above undocumented feature in a reply email. Basically this undocumented (AFAIK) feature allows you to add "v3.5" to the language definition in a template directive and it can be used, it seems like a bit of an after-thought but it does work.

One thing to note is that you can't specify extension method classes inside nested classes so it won't work in template include file, but you could externalise them to another assembly if required. So now you can add a class like the following to your code.

    1 namespace PGCodeWorks.Dsl.Test.TestDsl.TemplatingExtensions {

    2     public static class ExampleElementExtensions {

    3         public static string LowerCaseName(this ExampleElement element) {

    4             return element.Name.ToLower();

    5         }

    6     }

    7 }

And from your template code you can do the following.

   14     <#= element.LowerCaseName() #>

How nice is that? Should make templating a lot easier in VS2008.

Pete