Generating HTML emails with RazorEngine - Part 04 - Taking a step back: behind the scenes of Razor and RazorEngine

This is the fourth part of a 10-part blog series. You'll find a list of all the posts in this series in the introductory post. Make sure to review the Before we start section in the introductory post.

All the source code is on GitHub. Comment? Bug? Open an issue on GitHub.


Before diving any deeper into RazorEngine, it's worth spending a few minutes getting a good grasp of what Razor and RazorEngine really are and of how they work under the hood. It will save a lot of time and frustration when trying to customize or understand RazorEngine's behaviour.

This is a very quick and deliberately simplified overview of Razor.

What is Razor?

Razor is a language that lets you create document templates mixing static markup and code. Typically, the static markup is HTML and the code is C# or VB.NET. But it doesn't have to be. Razor is (sort of) language agnostic.

In practice, some of the rules that Razor uses to differentiate between static markup and code mean that it's better suited for templates where the markup is XML-like. And the Razor parser currently provided by Microsoft only supports parsing templates that use either C# or VB.NET. Nothing prevents your from extending it to support additional languages of course. But in practice, Razor is definitely best suited for document template that use either XML or HTML together with either C# or VB.NET.

The Razor syntax is very simple. In fact, it's essentially comprised of a single character: @ (sometimes accompanied with braces () or curly braces {} to delimitate respectively expressions that contain spaces or blocks of code).

In particular, it's important to realize that in the expressions you commonly use in your ASP.NET MVC Razor views such as @Html, @Url or @Model, the only part that Razor understands is the @ prefix. This tells the Razor parser that what follows is C# (or VB.NET) code. The Html, Url or Model parts on the other side aren't something that Razor understands (or care about).

The Razor parser is implemented in the System.Web.Razor.Parser.RazorParser class, which is part of the System.Web.Razor assembly. Although it was created to support the new Razor view engine introduced in ASP.NET MVC 3, it's not tied to the ASP.NET MVC framework. It can be used by any application to parse Razor template files, including console apps or Windows Services.

This is what the RazorEngine library relies on to provide its Razor-parsing abilities.

How is a Razor template transformed into the final HTML document?

The process of parsing and executing a Razor template to generate the final HTML document is deceptively simple.

  1. The Razor parser parses your template and generate the source code of a class that contains an Execute() method that, when run, generates the final HTML document.
  2. That class is then compiled on-the-fly into its own assembly using the compilation services provided in the System.CodeDom.Compiler namespace.
  3. Once compiled, the assembly is loaded into the app domain and the class is instantiated.
  4. The class' Execute() method can now be called to generate the final HTML document.

But an example is worth a thousand words.

Seeing the Razor parser in action

In the accompanying source code on GitHub, you'll find a small console app in the Part 04 folder that shows the Razor parser in action.

This application parses the following Razor template:

@model ConsoleApplication.Models.WelcomeModel

@{
    Layout = "MyLayout";
}

<h2>  
    Hi @Model.UserName,
</h2>  
<div>  
    Welcome here
</div>

@{
    var winner = new Random().Next() == 42;
}

@if (winner)
{
    <div>
        Congratulations! You're a winner. Choose your gift:
        @foreach (var gift in Model.Gifts)
        {
            <p>
                @Html.ActionLink(gift.Description, "Details", "Gifts", new {giftId = gift.Id})
            </p>
        }
    </div>
}

It first parses it using the Razor view engine that the ASP.NET MVC framework uses to render Razor views. It then parses it again, this time using the RazorEngine library.

If you run this app and look at the source code generated by the ASP.NET MVC Razor View Engine, you'll find the following generated class:

public class _Page_ : System.Web.Mvc.WebViewPage<ConsoleApplication.Models.WelcomeModel>  
{
    protected System.Web.HttpApplication ApplicationInstance
    {
        get
        {
            return ((System.Web.HttpApplication)(Context.ApplicationInstance));
        }
    }

    public override void Execute()
    {
        Layout = "MyLayout";
        WriteLiteral("\r\n\r\n<h2>\r\n    Hi ");
        Write(Model.UserName);
        WriteLiteral(",\r\n</h2>\r\n<div>\r\n    Welcome here\r\n</div>\r\n\r\n");

        var winner = new Random().Next() == 42;

        WriteLiteral("\r\n\r\n");

        if (winner)
        {
            WriteLiteral("    <div>\r\n        Congratulations! You\'re a winner. Choose your gift:\r\n");

            foreach (var gift in Model.Gifts)
            {
                WriteLiteral("            <p>\r\n");
                WriteLiteral("                ");
                Write(Html.ActionLink(gift.Description, "Details", "Gifts", new { giftId = gift.Id }));
                WriteLiteral("\r\n            </p>\r\n");
            }

            WriteLiteral("    </div>\r\n");

        }
    }
}

As you can see, there's neither magic nor any clever tricks here. The Razor parser generated the source code of a class deriving from System.Web.Mvc.WebViewPage<T> (which declares an abstract Execute() method via its WebPageExecutingBase base class) and implemented the Execute() method in the simplest possible way by just progressing linearly through the template file and:

  • Writing any static markup (HTML in this case) verbatim to the output stream
  • Inserting any code block or expression (C# in this case) verbatim into the body of the Execute() method of the generated class.

How does the Razor parser know whether it's reading static markup or code? Thanks to the @ character. Whenever it encounters @, it knows that what follows is code that should be inserted verbatim in the body of the Execute() method of the generated class. Whenever it encounters a space or a brace / curly brace signaling the end of a code expression / block, it switches back to static markup mode.

There are of course quite a few more twists involved but this is the gist of it.

When the ASP.NET pipeline reaches the point when the response body needs to be written to the output stream, it simply calls the Execute() method of this generated class, which writes the final HTML document.

It's clear when seeing what the Razor parser actually does that the Html, Url or Model expressions in your Razor views have nothing to do with Razor. They're simply properties of the WebViewPage base class that the generated class derives from.

How RazorEngine differs from the Razor view engine used by ASP.NET MVC

The RazorEngine library parses Razor templates using the same RazorParser class that ASP.NET MVC uses. With one big twist.

The console app for Part 04 in the accompanying source code parses the Razor template above using both the ASP.NET Razor view engine and the RazorEngine library.

If you run the app and look at the source code generated by the RazorEngine libray, you'll find:

public class RazorViewGeneratedByRazorEngine : RazorEngine.Templating.TemplateBase<><ConsoleApplication.Models.WelcomeModel> {

    public override void Execute() {
        Layout = "MyLayout";
        WriteLiteral("\r\n\r\n<h2>\r\n    Hi ");
        Write(Model.UserName);
        WriteLiteral(",\r\n</h2>\r\n<div>\r\n    Welcome here\r\n</div>\r\n\r\n");

        var winner = new Random().Next() == 42;

        WriteLiteral("\r\n\r\n");

        if (winner)
        {
            WriteLiteral("    <div>\r\n        Congratulations! You\'re a winner. Choose your gift:\r\n");

            foreach (var gift in Model.Gifts)
            {
                WriteLiteral("            <p>\r\n");
                WriteLiteral("                ");
                Write(Html.ActionLink(gift.Description, "Details", "Gifts", new {giftId = gift.Id}));
                WriteLiteral("\r\n            </p>\r\n");
            }

            WriteLiteral("    </div>\r\n");
        }
    }
}

As expected, the generated code is almost identical to that generated by the ASP.NET Razor view engine. The only notable difference is that the generated class derives from RazorEngine.Templating.TemplateBase<T> instead of System.Web.Mvc.WebViewPage<T>.

TemplateBase<T> is a custom base class that's part of the RazorEngine library. The RazorEngine library configures RazorParser to use this custom base class when generating code.

And this is the cause of most of the confusion, frustration and runtime exceptions that inevitably arise when using the RazorEngine library to parse Razor templates.

If you compare the interface exposed by TemplateBase<T> and WebViewPage<T>, you'll see that they're quite similar. For example, they both expose Layout and Model properties as well as RenderBody() and RenderSection() methods.

So using @Layout, @Model, @RenderBody() or @RenderSection() in your Razor template will work just fine when parsed with the RazorEngine library.

But try to use @Html (for example) in a template parsed with RazorEngine and your application will blow up at runtime with:

RazorEngine.Templating.TemplateCompilationException: Unable to compile template. The name 'Html' does not exist in the current context  

The reason for this should now be obvious: Html isn't a part of Razor but is a property of the WebViewPage<T> base class that ASP.NET MVC uses for its Razor views. Since the RazorEngine libary specifies its own base class TemplateBase<T> for the generated code and since this base class doesn't expose a property named Html, compilation of the generated class fails.

Why TemplateBase<T>

So why did the developer of the RazorEngine library choose to implement his own base class for the generated template classes instead of just using the WebViewPage<T> base class that the ASP.NET MVC Razor view engine uses?

Simply because WebViewPage<T> is heavily tied to the ASP.NET MVC framework and assumes that it's used within the context of a web request. You can explore its source code to see it by yourself.

Since the RazorEngine library is meant to be a general-purpose Razor parsing library that can be used within any application, including console applications or Windows Services, using WebViewPage<T> wasn't an option.

Hence the creation of TemplateBase<T> that implements many of the functionalities of WebViewPage<T> but leaves out web-specific functionalities, such as WebViewPage's Html and Url properties.

In practice, it means that most of the Razor templates you would write to use as views in your ASP.NET MVC applications can't be parsed with the RazorEngine library as they most likely use properties or methods of the WebViewPage<T> class that don't exist in RazorEngine's TemplateBase<T>.

When writing Razor templates that will get parsed with RazorEngine, make sure to restrict yourself to the properties and methods exposed by TemplateBase<T>.

Thankfully, the RazorEngine library lets you easily specify another base class to use for the generated code. So if you really want to be able to use @Html or @Url in your Razor templates, you can do it with not too much work. We'll see a first example of this in the next post in this series.

Further reading

If you'd like to learn more about how Razor works behind the scenes, look no further than Andrew Nurses's blog. Andrew is the main developer of the ASP.NET Razor view engine and has written a series of excellent articles on Razor: