Sending emails or other personalised messages from an application is a very common task. This post shows how Single-File Generators (SFGs) and Visual Studio can be used to keep email templates both readable and type-safe.
The Problem
Emails are important to get right and easy to get wrong. They are often public facing communications from your company and mistakes will reflect badly. I recently received an email from a large company that ended ‘Sincerely, {3}’. It’s enough to ruin the illusion of a personal email that the company’s marketing team spent many man hours creating.
An email might be written like this in C#:
It’s not particularly readable and it’s definitely not maintainable. If you have to add a new line or sentence, can you be sure you have the correct number of line breaks? Have you missed a full stop? It’s hard to say. This example is simpler than most real emails. A more typical example might contain hundreds of lines of HTML and would be far less readable.
Fixing the Problem
SFGs take a single file and allow you to transform it in an arbitrary way. I want to create one that takes a readable email template and generates .NET code that I can use in an application. For example, here is the above template in a readable text file called OrderConfirmation.txt:
Hello {string name}
Your order for a {string productName} was dispatched on {DateTime dispatchDate:dd MMM}.
Please allow {int deliveryDays} days for delivery.
Regards
The Shop
This is plain text with placeholders for variables that also contain type annotations, a much more reasonable way to store an email template. Once I’ve installed my SFG, I set the ‘Custom Tool’ property to ‘EmailGenerator’ (the name of my SFG) then when I save this file, OrderConfirmation.cs is automatically generated for me:
Now the horrible concatenated mess above has been reduced to a readable template class that can be used like so:
Readable, type-safe and maintainable!
Creating a Single File Generator
I recently installed Visual Studio Community 2013. It isn’t as ‘stripped back’ as the ‘express’ editions. It’s another great move towards making the .NET stack more accessible and appealing to the open source community.
The express editions do not allow you to target the Visual Studio SDK but the community (and paid) editions can. This means it’s possible to write Visual Studio plugins without spending a penny. To create a plugin containing an SFG, I installed the Visual Studio SDK and created a new project using the template ‘Visual Studio Package’. When the project is built, a ‘.visx’ file is generated that I can use to install my plugin.
Most SFG tutorials state you should implement IVsSingleFileGenerator and register your generator in the registry. This solution works however it is very low-level. If you implement IVsSingleFileGenerator directly you will have to deal with pointers and memory allocation. If I wanted to do that, I wouldn’t be writing C#. Thankfully there is a class called BaseCodeGeneratorWithSite that implements IVsSingleFileGenerator and gives us a much simpler interface to work with. Here is my code generator called ‘EmailGenerator’:
Visual Studio uses COM to communicate with the single file generator hence the need for the various attributes on the class. The interesting attribute here is CodeGeneratorRegistration which registers the single file generator in the registry for you. The third argument of this attribute specifies under what context the SFG works. My particular SFG can produce VB or C# source code.
GetCodeDomProvider gets the appropriate CodeDomProvider instance for a given project. If my SFG is used in a C# project, it will return an instance of CSharpCodeProvider and if used in a VB project it will be of type VBCodeProvider. I don’t need to know what concrete implementation I’m using so I use the abstract CodeDomProvider type everywhere.
GetDefaultExtension returns the extension of the generated class file - ‘.cs’ for C# and ‘.vb’ for VB.
GenerateCode is where the work gets done. I use a two-step process of parsing the template into an object model and then subsequently use that object model to generate the code I need. That’s all there is to it!
Parsing a Template
I represent an email template using the following classes:
A template is made up of a single template string and multiple parameters (see the generated OrderConfirmation class above). Each parameter has a name, type and an optional format string.
The ParseTemplate method takes a string and produces an instance of EmailTemplate. It uses a couple of regular expressions for parsing The parsing code is neither interesting nor clever but I’ve included it for completeness. You are encouraged to neither read nor judge it.
Generating Code
The code I want to generate is of the form:
The code to generate such a class is quite verbose however it is also fairly simple. CodeDomProvider contains methods for building a tree of all the needed parts. It also has a method to output to a TextWriter stream.
Conclusion
Single File Generators provide an automated way to generate code from arbitrary input files inside Visual Studio. I used a custom templating engine however this solution could be adapted to use something more familiar such as ‘Razor’. Even if you are not using Visual Studio or don’t want your project to depend on a plugin, I implore you to leave large amounts of text outside of code files.
When is it time to make something a generic re-usable class? And how do you manage the additional complexity of a generic solution? A potential solution lies in an incremental approach.
This blog post discusses the changes to the .NET framework, the solution layout / configuration and serves as an introduction to the recommended programming style encouraged by Microsoft going forwards.