The sentayho.com.vn team has developed a parser for Razor which is independent from sentayho.com.vn. This means that
The sentayho.com.vn team has developed a parser for Razor which is independent from sentayho.com.vn. This means that Razor can be used outside the web applications to generate documents, for example, e-mails in HTML.
In this entry, we will describe how to create e-mails in HTML format using Razor’s parser, outside a web project, and using layouts and importing classes inside the templates. Using RazorEngine to generate e-mails through Razor is quite simple, although the documentation provided by this library is rather limited. The aim of this post is to provide a little more information.
Conventions
The code is based on RazorEngine 3.4, Razor 3.0. The whole code was written in C#. By convention, all the files for our templates are the type .cshtml – such as the sentayho.com.vn MVC view. For MSBuid to be able to copy the file templates in the right place, it is necessary to set the property of the file “Copy to Output Directory” to “copy if it is newer”.
Step by step
RazorEngine gives us the class TemplateService to generate the desired document. The method used is Parse, which has 4 parameters.
- The first parameter is a string which represents the template. In this case it is a constant, but it can also be obtained from a cshtml.
- The second parameter is the model associated to the template.
- The third parameter is a DynamicViewBag, which is not used in this explanation.
- The last parameter is the name used to refer to the object in the cache.
using (var service = new TemplateService()) { const string template = “
Hello @Model.Number
”; const string expected = “
Hello
”; var model = new { Number = (int?)null }; string result = sentayho.com.vne(template, model, null, null); sentayho.com.vn(result == expected, “Result does not match expected: ” + result); }
To get to generate the document, RazorEngine takes the following steps: • It parses the template and generates the font code of the class that creates the final document. • It compiles this type “on the fly” inside its own assembly. • It uploads the assembly again inside the app of the applications domain. • It instantiates this new class. • It uses it to generate the final document.
Steps 2 and 3 are the most difficult ones, even with a simple template it could be quite slow. Fortunately, RazorEngine solves this problem in a simple way: you just have to make sure you always specify a cache name when you call sentayho.com.vn ().
In the current version of RazorEngine (3.6), each instance of TemplateService keeps its own cache. Therefore, the same TemplateService instance could be used during the complete life cycle of the application. If you are using a DI container, TemplateService could be created as a Singleton instance.
EmailResolved = sentayho.com.vne(template, model, null, “template_cached”);
It should be taken into consideration that there can only be one template for each name chosen to specify the cache. Therefore, the only thing RazorEngine can do is to overwrite the values of the model on the template. However, if you want to use another template with the same name “template_cached” there will be an error, for example:
EmailResolved = sentayho.com.vne(otro_template, model, null, “template_cached”);
As a consequence, if it is necessary to optimize time, a cache entry is advisable for each e-mail template.
the cache. To get to generate the document, RazorEngine takes the following steps: • It parses the template and generates the font code of the class that creates the final document. • It compiles this type “on the fly” inside its own assembly. • It uploads the assembly again inside the app of the applications domain. • It instantiates this new class. • It uses it to generate the final document.
Steps 2 and 3 are the most difficult ones, even with a simple template it could be quite slow. Fortunately, RazorEngine solves this problem in a simple way: you just have to make sure you always specify a cache name when you call sentayho.com.vn ().
In the current version of RazorEngine (3.6), each instance of TemplateService keeps its own cache. Therefore, the same TemplateService instance could be used during the complete life cycle of the application. If you are using a DI container, TemplateService could be created as a Singleton instance. It should be taken into consideration that there can only be one template for each name chosen to specify the cache. Therefore, the only thing RazorEngine can do is to overwrite the values of the model on the template. However, if you want to use another template with the same name “template_cached” there will be an error, for example:
As a consequence, if it is necessary to optimize time, a cache entry is advisable for each e-mail template.
Using namespaces
The template is initially turned into a font code file and then it is compiled dynamically invoking the compiler. Given that a font code can be used inside a template, it can use any library. However, the compiler should be able to solve them, and the default strategy is to make reference to all the assemblies previously uploaded.
This can bring about some problems, if in the template you want to use a library that was not referred to in the hosting code or if it was not uploaded in the execution time (since it is not used). To be able to solve this kind of issues, that behavior can be controlled and a custom implementation of the IReferenceResolver interface can be set. The following implementation was done:
class CustomReferenceResolver : IReferenceResolver { private static string RazzorAssemblies = “RazorAssemblies”; private static readonly ILog Log = sentayho.com.vnogger(MethodBase.GetCurrentMethod().DeclaringType); public string FindLoaded(IEnumerable
Here a section is defined in the sentayho.com.vnig, called “RazorAssemblies” with the dlls needed. If the assembly has already been uploaded, the reference should be added. Otherwise the assembly and the reference should be added. In the sentayho.com.vnig, a section was added:
In which a further indirection level is added, so, if in the future it is necessary to add more libraries, the sentayho.com.vn will not be modified.
The sentayho.com.vn file includes the following:
In the first example, it was explained that the class to be used is TemplateService. As a new IReferenceResolver implementation is to be added, when the instance is created it is necessary to pass the new configuration per parameter. To that end, the following method is defined, where the namespaces to be used by the templates are added, as well as the configuration of the new ReferenceResolver.
private TemplateServiceConfiguration GetTemplateServiceConfiguration() { ICollection
Creating Emails Using Layout
We define some constants in the following way, and then we define the following method.
private readonly string MessagePath = @”LayoutMessage.cshtml”; public string CreateEmail(object model, string templatePath){ TemplateServiceConfiguration config = GetTemplateServiceConfiguration(); var emailResolved = sentayho.com.vny; var messageTemplatePath = sentayho.com.vnine(AppDomain.CurrentDomain.BaseDirectory, MessagePath); var footerTemplatePath = sentayho.com.vnine(AppDomain.CurrentDomain.BaseDirectory, FooterPath); var headerTemplatePath = sentayho.com.vnine(AppDomain.CurrentDomain.BaseDirectory, HeaderPath); var templateFilePath = sentayho.com.vnine(AppDomain.CurrentDomain.BaseDirectory, templatePath); var template = sentayho.com.vnAllText(templateFilePath); var messageTemplate = sentayho.com.vnAllText(messageTemplatePath); var footerTemplate = sentayho.com.vnAllText(footerTemplatePath); var headerTemplate = sentayho.com.vnAllText(headerTemplatePath); using (var service = new TemplateService(config)) { sentayho.com.vnemplate(messageTemplate, null, Message); sentayho.com.vnemplate(footerTemplate, null, Footer); sentayho.com.vnemplate(headerTemplate, null, Header); emailResolved = sentayho.com.vne(template, model, null, null); } return emailResolved; }
The CreateEmail method receives as a parameter an object which is used in the template and the templatePath parameter, which is the path relative to the cshtml, from the corresponding email. Layouts have the following structure, please check that the value assigned to the layout of sentayho.com.vn is Header, the layout of Header is Footer, and the layout of Footer is Message. The service method GetTemplate saves all the templates.
Bear in mind that we have an instance of templateService for each execution, and that can be improved. If only one TemplateService is created for the whole application and there is a cached entry for each template, it will be much more efficient.
sentayho.com.vne(template, model, null, “cache1”); sentayho.com.vne(template2, model, null, “cache2”);
Footer.cshtml
@using sentayho.com.vn @using sentayho.com.vnurces @{ Layout = “Message”;} @section Footer{
}@RenderBody()
Header.cshtml
@{Layout = “Footer”;} @section Header { Aca va el header }
Message.cshtml
And this is the body of the email.
@model sentayho.com.vnserClientModel @using sentayho.com.vn @using sentayho.com.vnurces @{Layout = “Header”;} @section Body {
}
This way, by using Layouts we can simplify our templates. We had to use this tool to send emails, and layouts templates proved to be very useful.
Bibliography:
- Generation HTML emails with razorengine
- Antaris RazorEngine, Site Layouts
- Razon Engine – Reference Resolver
See All Posts