Rendering Templates in a seperate AppDomain

The RazorEngine can be loaded into a separate AppDomain to provide unloading support and additional security.

The easiest way to load templates in an AppDomain is by using a HostContainer. HostContainers have a UseAppDomain flag that you can set and if true automatically loads the RazorEngine in an AppDomain.

It's as simple as:

hostContainer.UseAppDomain = true;

If you do, make sure any models passed into the host for rendering are marked [Serializable] or inherit from MarshalByRefObject. Due to these limitations you have to ensure that all parameters and dependent members can be serialized when passing parameters from your application to the RazorEngine via AppDomains.

Using an AppDomain is useful when loading lots of templates and allows for unloading the engine to reduce memory usage. It also helps isolate template code from the rest of your application for security purposes, since Razor templates essentially can execute any code in the context of the host.

Using RazorEngineFactory to create RazorEngine in an AppDomain

You can also do this manually at the lower level RazorEngine level, by using RazorEngineFactory. This class creates a new RazorEngine instance using several different host models, including loaded inside of an AppDomain.

This is not recommended unless you plan to build a custom HostContainer. The provided HostContainer implementations wrap the AppDomain and Engine instances and hide the lower level behavior from the consumer of the container.

Using RazorEngineFactory

When RazorEngine is loaded it's loaded into the current AppDomain of the application. Since everytime you compile a template an assembly is created and loaded in that AppDomain the assembly can not be unloaded which can result in memory issues if lots of templates are run.

To mitigate this problem you can load the RazorEngine instance into a separate AppDomain which can be unloaded:

string template = @"Hello World @Model.Name. Time is: @DateTime.Now";
            
// Load engine into new AppDomain
var host = RazorEngineFactory<RazorTemplateBase>.CreateRazorHostInAppDomain();
            
string result = host.RenderTemplate(template, new Person { Name = "Joe Doe" });
            
Assert.IsNotNull(result, host.ErrorMessage);
Assert.IsTrue(result.Contains("Joe Doe"));

// shut down AppDomain
RazorEngineFactory<RazorTemplateBase>.UnloadRazorHostInAppDomain();

Console.WriteLine(result);

This creates a new AppDomain, loads the RazorEngine into it and then runs the RenderTemplate operation - along with the compilation process and new assembly loaded - in the new AppDomain.

You can run a single or many RenderTemplate() operations as necessary. When done or when memory usage gets high, simply call UnloadRazorHostInAppDomain to release the AppDomain instance, which releases the AppDomain and all of the loaded assemblies.

Why use an AppDomain?

Using an AppDomain has extra setup and tear down overhead and rendering can be a little slower. But it does allow more control over the lifetime of objects and how memory is managed. Especially when rendering templates from strings it's often useful to allow unloading of AppDomains to reclaim memory.

In addition, remember that Razor is in effect a scripting engine that executes .NET code. By running in a separate AppDomain the host application is effectively off limits to the script code, having access only to the model/template that you pass to the template. This reduces the security risk and scripting attacks against your application.

For serious applications especially those involving end user code it's recommended you run in an AppDomain.


© West Wind Technologies, 2018 • Updated: 05/29/17
Comment or report problem with topic