Using Razor Layouts With Insite Emails

Insite's EmailService allows a developer to easily send emails to a customer whenever they perform an action on the system. This EmailService utilizes the RazorEngine in order to parse HTML and generate rich content emails for delivery. On a recent project, I had many scenarios where an email was being sent to the customer. All of the emails contained a common look and feel, including a fully rendered header and footer. In this post, I will go over the process I followed in order to allow for the use of the standard Layout property in the Razor template for emails.

When the email templates were first created, I immediately tried to utilize the standard Layout property for a Razor view in order specify which content it should use.

Layout File:


This is the common header for all of the emails sent from Insite
This is the common footer for all of the emails sent from Insite

Email Template

    Layout = "~/Views/DefaultEmails/MainEmailLayout.cshtml";

Unfortunately, this immediately started generating errors in the EmailService.



Turns out, the relative path specified is a part of the Insite web application, and isn't resolvable from the context in which the EmailService executes. After looking through the OOTB EmailService code, I was able to determine that the content parser could be customized.


public class EmailServiceRio : EmailService
    public EmailServiceRio(IEmailTemplateUtilities emailTemplateUtilities, IContentManagerUtilities contentManagerUtilities, IEntityTranslationService entityTranslationService)
        : base(emailTemplateUtilities, contentManagerUtilities, entityTranslationService)

    // Insite apparently has issue with using Layout in the Razor.
    // We are overriding their parse template method to manually resolve and inject the Layout content.
    public override string ParseTemplate(string template, ExpandoObject model, string templateName)
        #region Customize - Allow for Layouts in templates

        var noLineTemplate = template.Replace("\r", "").Replace("\n", "").Replace("\t", "");

        // @{    Layout = "~/Views/DefaultEmails/EmailMainLayout.cshtml"; }
        var layoutRegex = new Regex("@{\\s*?Layout\\s*?=\\s*?\"(?.*?)\";\\s*?}");

        // If we are using a layout
        if (layoutRegex.IsMatch(noLineTemplate))
            var filePath = layoutRegex.Match(noLineTemplate).Groups["filePath"].Value;

            noLineTemplate = layoutRegex.Replace(noLineTemplate, string.Empty);

            var layoutLines = string.Join(Environment.NewLine, File.ReadLines(HostingEnvironment.MapPath(filePath)).ToList());

            template = layoutLines.Replace("@RenderBody()", noLineTemplate);


        var content = Razor.Parse(template, model, templateName);

        return content;
The above code uses a Regular Expression and some basic string manipulation magic in order to detect and resolve a layout file being referenced in a email template. As mentioned above, the normal execution context of the EmailService means that it will not have access to a web based relative path to retrieve the content. In order to get the file, I need to get the fully qualified path for the layout file (e.g. C:\Projects\InsiteCommerce-4.2.0\InsiteCommerce.Web\Views\DefaultEmail\MainEmailLayout.cshtml). Once I have the fully qualified path for the layout, I can manually read the file off of the server. By simply replacing "@RenderBody()" from the layout file with the original template content, I now have a fully formed email ready to be parsed and delivered.


There you have it. With the help of regular expressions and a some string manipulations, I now how a solution that allows me to utilize Razor Layouts in my email templates, saving me a lot of work when modifying the content of my emails.

The example used in this blog post has been implemented on Insite version, a MVC application running on .NET Framework 4.5.2.
Tags: email, ".NET", Insite;