Skip to main content

Cloud

Easy Email Templates with the Razor View Engine

    I was given a task recently to have an application generate and send an email. The requirements were straight forward, the email should contain some customer information along with a list of products. My first thought was to use StringBuilder to handle the task, but something about that bothered me. In 2013, am I still building markup by hand? That just felt wrong!
The email is a fairly simple one, so using StringBuilder wasn’t out of the question, but who wants to mess up their well-written, well-formatted C# code with a bunch of messy hard-coded HTML strings?
My first thought was to use XSLT. It’s pretty well-suited to the task at hand: you create a template, then serialize your data and use it to transform the template into HTML. I have three issues with that:

  1. My data is coming from multiple sources, so I would need to create a single class that holds all my data
  2. I would have to re-learn XSLT
  3. Whoever comes behind me in the future would need to also know XSLT

The first issue is very minor, more of an annoyance really. The second issue is there because although I’ve used XSLT in the past, it has been years, and I would essentially need to re-learn it in order to use it. The third issue is that I don’t feel XSLT is a common skill among developers. You could go your entire development career and never touch it.
I remember reading somewhere online a while ago about using Razor outside the context of MVC. Razor is designed to generate HTML and gives the developer the entire power of the .NET Framework (types, methods, etc.). MVC has become extremely popular, and so has Razor, so there is a pretty good chance that the developer who comes behind me will know it. At the very least, they will know HTML and C#, which will make viewing/editing the template fairly straight-forward.
What is Razor?
Razor is a view engine developed to be used with ASP.NET MVC. When ASP.NET MVC was first introduced, it was packaged with the WebForms view engine (since there is no code behind, it ends up looking a lot like classic ASP). One of the tenants of MVC is separation of concerns. Because of this, the MVC team made sure that if someone wanted to write their own view engine, they could do so and plug it into the MVC framework. Sometime later, Microsoft released their own alternate view engine called “Razor”. One of the primary goals for Razor was to have a much cleaner, easier to type syntax, here’s a quick comparison of the Razor and WebForms:
WebForms:
<a href=”<%= Model.NavigateUrl %>”><%: Model.Name %></a>
Razor
<a href=”@Model.NavigateUrl”>@Model.Name</a>
Not only is the Razor syntax easier to read, but if you think about actually typing the two lines out, it is also much easier to write.
How does Razor work?
It’s important to understand how Razor actually works. I’ll walk through the engine from the perspective of an MVC application (its typical use).
Once the controller requests that a view is rendered, MVC determines that the view is a Razor view and invokes the Razor view engine. The engine will read the template, convert it into a C# class (or VB.NET, depending on the file extension). The code is then compiled into an assembly and the assembly is cached. The generated class is then invoked and the output is written to the response stream. Anything in the template that is indicated as code (using the @ prefix) is left as code to be executed, and everything else is turned into literal strings. If the file is changed, the Razor view engine is notified that the file has changed, the cached assembly is dumped and the next time the view is requested, it will generate a new assembly.
So we’re going to do all that?
No. All of those steps seemed like overkill, and when I searched online, I didn’t see any easy way to implement all of those steps. We’re going to precompile the view into our application, and use it like we would any other standard C# class. The up-side to this approach is that there isn’t any initial runtime cost like there is with MVC. The downside is that if you want to change the template, you have to recompile your code and deploy. That may sound like a lot of work if you want to make a simple change, but assuming you are making the change in a production environment, do you really want to be able to make a change that easily (and possibly introduce bugs?) – not I!
That’s nice… so how do I create an email using Razor?
Now we are getting to the meat of the subject. For this post I will use a console application. It’s the simplest type of application, and therefore would likely require the most amount of setup. If we can get this to work in a console application, we can get it to work anywhere.
Since we will need to generate the C# code from the template up-front, we will want to do it in Visual Studio. There is a Visual Studio extension, go to Tools -> Extensions and Updates… In the modal, click “Online” from the left-hand side, and at the top, search for “Razor Generator”. The extension you want is the one shown below. Install it. Restart Visual Studio of needed. If you don’t already have NuGet, you will need to download that extension as well.
 Extension 
Create your console application (I’m using Visual Studio 2012 and the 4.0 Framework – just because).
In your console application, right-click on References, and select “Manage NuGet packages…” Search for “RazorGenerator” (one word). The package you will want to download is called RazorGenerator.Templating.
 NuGet 
The RaorGenerator.Mvc package is for precompiling Razor views in an MVC application, which is outside the scope of this post.
When the package installs, it creates a file in your project called “SampleTemplate.cshtml”. Feel free to look at this file, but I usually delete it and start writing my own from scratch.
To create your own template, you will need to add a Razor file. Unless you are in an MVC project, you won’t be able to directly add one. I add a class file instead, but make sure it ends with the .cshtml extension.
Delete any text that is in your newly created file. In Solution Explorer, find your file, right-click and go to “Properties”. In the properties window, make sure it looks like this:
 Properties
When you created the file, if you selected “Class”, the only change you will need to make is that you will need to type “RazorGenerator” in the “Custom Tool” property. These settings tell Visual Studio to not attempt to compile the file and to not copy it to the output directory. It also tells Visual Studio to use the ReazorGenerator custom tool (added by the Visual Studio extension) whenever the file is updated (or the user right-clicks the file and selects “Run Custom Tool”).
The custom tool will generate the C# class file and you can view it by expanding the .cshtml file and seeing a generated file underneath. Do not manually edit the generated file! Any edits you make will be overwritten the next time the custom tool runs.
If you look at the generated file right away, it will have a message for you to help you understand why it can’t generate a code file.
The reason the NuGet package is needed is because it adds a reference in your project to RazorGenerator.Templating.dll. The class generated by the custom tool inherits from a class that is located in this assembly. If you don’t want to install the NuGet package, you can go to the project site (linked at the bottom of this poast) and download the DLL and manually add it to your project.
The Razor Generator extension has several providers that it can use to create the class file, but at this time it doesn’t know which one to use. Unlike MVC, your .cshtml file will need a comment on the first like, it will look like this:
This line tells Razor Generator to use the template provider.  In razor, @* this is a comment *@, so the line @* Generator: Template*@ is ignored by the Razor view engine, but the custom tool will read that line and know which provider to use.
Once that line is in place, save the .cshtml file and now look at the generated file, it is a C# class – though it doesn’t actually do much.
A few quick notes for those of you familiar with Razor in MVC:

  • You will not have access to any of the various helpers (Html, Url, Ajax, etc.)
  • None of the properties you are used to exist (Model, ViewBag, etc.)
    • The intellisense will include these properties, but that is because Visual Studio assumes .cshtml will be used in the context of MVC. The good news is that if you use one of these that don’t exist, it will generate a compiler error and not a runtime error, so you will be notified sooner rather than later.

You can create a web page like you normally would (add an html tag, head and body tag, etc). Save and compile.
In your Main method of the console application, create a new instance of your template (the name of the .cshtml file is the class name). The class will have a method called TransformText. You can invoke that and it will render the template. If you were to run the application now you would see your markup returned from the method.
At this time, this isn’t very exciting. You could have created a static file and included it with your project and you would have the same results. Let’s add some data!
In the template file you can declare fields, methods, properties, etc. You must do this in the special @functions{ .. } block. I usually put this near the top – after the generator comment, but before the markup begins.
 Functions
Notice that you can also put in using statements. This is useful so you don’t have to specify a namespace for all the different types you will be using.
My template will create a list of customers in a nice table, so I’ve added a property to my template called Customers. You can add fields as well as methods in this area. Anything declared in this block will be scoped to the class level. Save your change in order for those changes to be reflected in the generated file.
Now, if you go back to your Main method in the console application, you will now see that your template has a property on it called Customers. This is how you will pass data into your template. Now in your template, you can include a line like @Customers.Count() and when you run the template, that will output the number of customers in the IEnumerable<>.
Note: By default, the generator includes the following using statements:
 Usings
This is why I am able to use IEnumerable<> without typing out the namespace, and why I can invoke a LINQ method from my Customers property.
Layouts
Layouts are supported by the Template generator, but sections are not. This means you can have a layout, but you cannot call RenderSection; you can only call RenderBody.
Summary
With the power of the Razor view engine, you will be able to create templates for emails or any other need you may have. Does the project have nasty hard-coded markup and does it use some form a string concatenation? Sure, but it’s been abstracted away so that we don’t need to manually write it nor do we need to actually see it (unless we really want to), and that is more than good enough for me!
In order to edit the .cshtml file, the extension will need to be installed, but anyone can compile and deploy the template without having any special tools (e.g. automated builds machines). If you do not have the extension installed and you edit a .cshtml file that uses the generator, then no error will be shown, the file will be saved, but the generated class file will not be modified.
Related Links

Thoughts on “Easy Email Templates with the Razor View Engine”

  1. Very nice article. I want to use with C#. I have been trying to do the following with no success:
    Created a model:
    public class Dummy{Name{get;set;}
    Now instantiating this class:
    Dummy k = new Dummy{Name=”KK”;}
    Dummy l = new Dummy{Name=”pp”;}
    IList testList = new List{k,l};
    string template = @”@foreach(var item in Model){Hello @item.Name World!;}”
    string s = Razorengine.Parse<IList>(template,testList);
    Would be nice if I could get a solution to this:
    thanks

  2. hi Brian,
    happen to see your site and it is really very informative.
    btw i had a question when i downloaded the sample it always complains about
    The name ‘RenderSection’ does not exist in the current context
    Can you please let me know if I am missing any references.
    Thanks
    Anil

  3. Hey Anil, thanks for the question.
    At the bottom of my blog post, before the “Summary” section, I note that sections cannot be used with the Razor templates. I don’t know exactly where the short-coming is that prevents this. I didn’t dig that deep, but I know that it isn’t available.
    The reason you are getting an error is because when you use sections, under the hood, razor attempts to call a RenderSection method, but no such method is defined in the base class for the template.

  4. Hi Brian,
    I just found this post and it was helpful because I was trying to use Ajax.BeginForm inside a precompiled view and it blows up when compiling. Since the namespace System.Web.Mvc.Ajax is included in the configuration files, I would expect that helper to be available. Is there a way to write a local helper that wraps that functionality?
    Thanks,
    Walter

  5. Is this for the purposes of emailing, or are you just talking about precompiling templates to be served up by your web application?
    If it’s the former, I would not expect most/any email clients to support ajax requests (even if they do, you’ll likely have to configure CORS).
    If it’s the latter, then I would expect it would work, though if you are compiling them in a class library, then I’m not sure what configuration file you would be referring to. App.config files don’t work with libraries like they do executables, and web.config files don’t have any effect on a class library when it is being compiled. You can always try including the namespace directly in your .cshtml file and see if that helps.

  6. Hi! Nice post!
    I know this is a really old post but I would like to point out that the sample solution doesn’t build. It is confusing because the post says you can not use RenderSection, but the sample code uses it in the LayoutTemplate.cshtml. Maybe is an example showing things that do not work? The post also says the RenderBody should work, but it doesn’t. I am not sure if it is missing references or what.
    Anyhow ignoring the Layout example the rest is very useful. Thank you!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.