An early look at RazorEngine v3

An early look at RazorEngine v3
Finally, I’ve started making progress on getting RazorEngine v3 out into the wild. Last night I pushed an early version of v3 to RazorEngine’s new home at GitHub. There is still a lot of stuff I need to get done, but there is at least something you can start poking around with. There are a lot of changes in v3, so I thought I’d highlight just a few here.

Moving to GitHub
The first thing you’ll notice is that I’m no longer pushing the code onto pulmonary edema lasix new, but now moving to GitHub. GitHub will give us the opportunity to better share and collaborate with the development community to start really pushing RazorEngine in the open-source direction. Up until now, development has really been done by Ben (@buildstarted) and myself, but I want to allow many more people to get involved and determine the direction we take RazorEngine. GitHub’s fantastic SCM abilities and community features will help us achieve this.

Currently, I’ve been using the master branch for all my commits. After the release push of v3, we’ll start development in secondary branches and use the master branch as a release branch.

An early version of v3 is on GitHub now, so feel free to fork and start your pull requests, ask questions and get involved 🙂

Breaking API Changes
I hate breaking API changes, but unfortunately there is such a significant reason to do so that it becomes unavoidable. Attempting to wrap the older API calls into what we now need may be impossible to do cleanly. Rest assured, I’ll be doing a complete review of the API before pushing an RC, but unfortunately there have been a few required changes.

The key change that prompted this was the overwhelming need to run RazorEngine in parallel/multi-threaded scenarios. Because of how RazorEngine v2 was laid out under the hood, there were a number of mistakes that were made in its design, and thread-safety was a big one. Currently, attempting to run RazorEngine v2.1 in a multi-threaded way nearly always ends badly, you’ll get a hideous amount of mangled template output and/or failed compiles. This has prompted me to redesign the API to support multi-threaded scenarios natively.

This key change is breaking, and there a few other changes which have required a redesign. We’ll be reviewing the whole API and will document some migration guides nearer release date.

Native Parallel Support
To assist with parallel scenarios, I’ve baked in support for parsing multiple templates in parallel. This means you don’t have to worry about handling the threading model yourself, let RazorEngine’s TemplateService take care of it. Here is an example test highlighting running some templates in parallel:

<!-- wp:paragraph --> <p>/// /// Tests that the template service can parse multiple templates in parallel. ///<br>[Test]<br>public void TemplateService_CanParseMultipleTemplatesInParallel_WitNoModels()<br>{<br>using (var service = new TemplateService())<br>{<br>const string template = "Hello World";<br>var templates = Enumerable.Repeat(template, 10);</p> <!-- /wp:paragraph --> <!-- wp:code --> <pre class="wp-block-code"><code> var results = service.ParseMany(templates, true); Assert.That(templates.SequenceEqual(results), "Rendered templates do not match expected."); }</code></pre> <!-- /wp:code -->
Code language: HTML, XML (xml)

Obviously this is a trivial example, there are a number of overloads for the ParseMany method which supports scenarios with single templates/many models, many-templates/many models, etc. The parallelism is provided by PLINQ, and the execution of which can be customised by providing your own implementation of the new IParallelQueryPlan interface.

This is not to say you can’t use your own threading model, or even the threadpool. If you want to pop over to GitHub you can find some example tests of running a TemplateService in parallel scenarios. A future blog post will cover this in more depth.

Unit Test Support
At first I found it hard to quantify what would be considered a plausible test case. Originally we were simply providind a wrapper around the Razor parser, so most unit tests would only be testing the parser itself. As RazorEngine has evolved, a need has arrisen to provide a suite of tests that prove the API.

Currently I am using NUnit as the test framework, but I would imagine I will push up some sample tests using alternatives. This is important, as unit testing was a key area of support we were lacking in v2.1. There is a whole host of tests on GitHub currently, as these will expand as we introduce more features and capture more test scenarios.

Template Isolation
Razor was built around a neat parsing technology, and a code generation framework. Under the hood, your templates are being parsed, and compiled into executable class instances. Each template we generate is compiled into its own assembly, and subsequently loaded into memory. If you are using RazorEngine in volume you’ll notice that the memory footprint of your app will increase, because we do not have the ability to unload assemblies from the primary AppDomain.

In v3, we’ve introduced a new template service, the IsolatedTemplateService that supports the parsing of templates in a child AppDomain. Now, there are some limitations on what can be parsed, essentially at the moment there is no support for anonymous or dynamic models, and also any models that you do want parsed must be serialisable (for cross-domain communication).

Here is an example test of using the IsolatedTemplateService:

/// /// Tests that a simple template with a model can be parsed. /// [Test] public void IsolatedTemplateService_CanParseSimpleTemplate_WithComplexSerialisableModel() { using (var service = new IsolatedTemplateService()) { const string template = "Hello @Model.Forename"; const string expected = "Hello Matt"; var model = new Person { Forename = "Matt" }; string result = service.Parse(template, model); Assert.That(result == expected, "Result does not match expected: " + result); }
Code language: JavaScript (javascript)

The basic example is virtually identical to how you would instantiate a normal TemplateService, we wanted to make it easy to spin up instances in template services. A future blog post will detail AppDomain isolation more.

Automatic Text Encoding
Like ASP.NET MVC’s Razor implementation, we wanted to include automatic text encoding in the base framework. As the majority of use cases are based around the use of HTML, we’ve defaulted the default text encoding for values as Html-encoded. We also support raw-encoding (i.e. raw text) also, as well as a native Raw method for rendering out raw text in an Html-encoded template.

There is still a lot more to come, I hope to introduce more extensibility points to allow developers to plug more directly into the pipeline. Let me know your thoughts!

Leave a Reply

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

Related Post