Templated HTML emails using RazorEngine

Following on from my previous article[^] where I described how I used the Azure SendGrid service to send emails, I will now describe how I created the emails themselves. I wanted some way of templating the emails so that I could use a standard layout containing placeholders for information that I would supply at run-time.

In the old days of word-processing, this was called a mail merge. You have a templated document where data is then supplied to fill in the blanks. Instead of a Word document I had an HTML document, but the same principle applies. The HTML document contained placeholders where I would supply data at run-time. These templated HTML documents are called Razor documents.

Razor is a language that lets you create document templates mixing static markup and code. Typically, the static markup is HTMLand the code is C# or VB.NET. These can be as simple or as complex as you need. You have the full power of the .NET framework at your disposal, alongside HTML markup and CSS styling, so you can really go to town and create some amazing looking content. A full description of Razor is beyond the scope of this document, but there are planty of resources where you can dig deeper into this technology.

When I initially began looking into using Razor, I wanted the ability to create certain templated layouts using HTML, and then at run-time I would provide data to the layouts. This would allow me to create an HTML document that I could then set as the body of the email.

For the purposes of this article I will use the trivial Razor document I created for my unit test fixtures.

Hide Copy Code

@model Common.Models.EmailRequests.UnitTestModel

<h1>This is a Unit Test Email Razor Template</h1>
<h2>User Details</h2>
<h3>Name: @Model.Name</h3>
<h3>Company: @Model.Company</h3>
<h3>Telephone: @Model.Telephone</h3>

<h2>Car Details</h2>
<h3>Registration: @Model.Registration</h3>
<h3>Description: @Model.Description</h3>

And the corresponding model that is used to supply the data.

Hide Copy Code

namespace Common.Models.EmailRequests
{
public class UnitTestModel
{
public string Registration { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public string Forename { get; set; }
public string Surname { get; set; }
public string Telephone { get; set; }
public string Company { get; set; }
}
}

All very simple and straight forward.

The next step then is to actually create a templated document containing real data. During my investigations I came across RazorEngine[^]. This is a templating engine built on Microsoft’s Razor parsing engine and allows you to use Razor syntax to build dynamic templates. It gives you a layer of abstraction that makes using the Razor parsing engine extremely simple. It takes care of the necessary mundane chore of compiling and running your template. It can be installed into your Visual Studio project using the nuget package manager. Describing RazorEngine could take up an entire article on its own, so be sure to check out the linked Github page for further information about this excellent tool.

I wrote the following function which uses RazorEngine to create a fully populated Razor document using the specified Razortemplate and model.

Hide Expand

Image for post
Image for post

Copy Code

/// <summary>
/// Generate an HTML document from the specified Razor template and model.
/// </summary>
/// <param name="rootpath">The path to the folder containing the Razor templates</param>
/// <param name="templatename">The name of the Razor template (.cshtml)</param>
/// <param name="templatekey">The template key used for caching Razor templates which is essential for improved performance</param>
/// <param name="model">The model containing the information to be supplied to the Razor template</param>
/// <returns></returns>
public string RunCompile(string rootpath, string templatename, string templatekey, object model)
{
string result = string.Empty;

if (string.IsNullOrEmpty(rootpath) || string.IsNullOrEmpty(templatename) || model == null) return result;

string templateFilePath = Path.Combine(rootpath, templatename);

if (File.Exists(templateFilePath))
{
string template = File.ReadAllText(templateFilePath);

if (string.IsNullOrEmpty(templatekey))
{
templatekey = Guid.NewGuid().ToString();
}
result = Engine.Razor.RunCompile(template, templatekey, null, model);
}
return result;
}

The above function returns a string containing the HTML markup for a fully transformed Razor document. Here’s an example of what is returned.

Hide Copy Code

<h1>This is a Unit Test Email</h1>
<h2>User Details</h2>
<h1>Name: Mr Unit Test</h1>
<h1>Company: Unit Test Company</h1>
<h1>Telephone: 01536536536</h1>

<h2>Car Details</h2>
<h1>Registration: UT01UNIT</h1>
<h1>Description: Ford Mustang</h1>

This then becomes the body of the email.

An excellent article that I found very useful was this one[^]. Pay particular attention to Chapter 3 where the author describes the performance gains when caching is implemented. To load a Razor template, as with loading any resource, takes up valuable resources. It can be a relatively slow process if you always load up your templates from disk every time. Far better therefore to cache your Razor templates so that they can be loaded far quicker.

Although I have only described a very simple Razor template and model, the same principles can be applied to far more complex templates. RazorEngine really does take the hard work out of manipulating the Razor parsing engine and if considering using the Razor parsing engine then I would definitely suggest using RazorEngine.

I have now implemented a fully functioning HTML templating engine complete with massively scalable email to send out those emails thanks to Azure SendGrid and Razor.

A father, cyclist, vegetarian, atheist, geek and multiple award winning technical author. Loves real ale, fine wine and good music. All round decent chap.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store