Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

Hosting the Razor Engine for Templating in Non-Web Applications


Microsoft’s new Razor HTML Rendering Engine that is currently shipping with ASP.NET MVC previews can be used outside of ASP.NET. Razor is an alternative view engine that can be used instead of the ASP.NET Page engine that currently works with ASP.NET WebForms and MVC. It provides a simpler and more readable markup syntax and is much more light weight in terms of functionality than the full blown WebForms Page engine, focusing only on features that are more along the lines of a pure view engine (or classic ASP!) with focus on expression and code rendering rather than a complex control/object model. Like the Page engine though, the parser understands .NET code syntax which can be embedded into templates, and behind the scenes the engine compiles markup and script code into an executing piece of .NET code in an assembly.

Although it ships as part of the ASP.NET MVC and WebMatrix the Razor Engine itself is not directly dependent on ASP.NET or IIS or HTTP in any way. And although there are some markup and rendering features that are optimized for HTML based output generation, Razor is essentially a free standing template engine. And what’s really nice is that unlike the ASP.NET Runtime, Razor is fairly easy to host inside of your own non-Web applications to provide templating functionality.

Templating in non-Web Applications? Yes please!

So why might you host a template engine in your non-Web application? Template rendering is useful in many places and I have a number of applications that make heavy use of it. One of my applications – West Wind Html Help Builder - exclusively uses template based rendering to merge user supplied help text content into customizable and executable HTML markup templates that provide HTML output for CHM style HTML Help. This is an older product and it’s not actually using .NET at the moment – and this is one reason I’m looking at Razor for script hosting at the moment.

For a few .NET applications though I’ve actually used the ASP.NET Runtime hosting to provide templating and mail merge style functionality and while that works reasonably well it’s a very heavy handed approach. It’s very resource intensive and has potential issues with versioning in various different versions of .NET. The generic implementation I created in the article above requires a lot of fix up to mimic an HTTP request in a non-HTTP environment and there are a lot of little things that have to happen to ensure that the ASP.NET runtime works properly most of it having nothing to do with the templating aspect but just satisfying ASP.NET’s requirements.

The Razor Engine on the other hand is fairly light weight and completely decoupled from the ASP.NET runtime and the HTTP processing. Rather it’s a pure template engine whose sole purpose is to render text templates. Hosting this engine in your own applications can be accomplished with a reasonable amount of code (actually just a few lines with the tools I’m about to describe) and without having to fake HTTP requests. It’s also much lighter on resource usage and you can easily attach custom properties to your base template implementation to easily pass context from the parent application into templates all of which was rather complicated with ASP.NET runtime hosting.

Installing the Razor Template Engine

You can get Razor as part of the MVC 3 (RC and later) or Web Matrix. Both are available as downloadable components from the Web Platform Installer Version 3.0 (!important – V2 doesn’t show these components). If you already have that version of the WPI installed just fire it up. You can get the latest version of the Web Platform Installer from here:

http://www.microsoft.com/web/gallery/install.aspx

Once the platform Installer 3.0 is installed install either MVC 3 or ASP.NET Web Pages. Once installed you’ll find a System.Web.Razor assembly in C:\Program Files\Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies\System.Web.Razor.dll which you can add as a reference to your project.

Creating a Wrapper

The basic Razor Hosting API is pretty simple and you can host Razor with a (large-ish) handful of lines of code. I’ll show the basics of it later in this article. However, if you want to customize the rendering and handle assembly and namespace includes for the markup as well as deal with text and file inputs as well as forcing Razor to run in a separate AppDomain so you can unload the code-generated assemblies and deal with assembly caching for re-used templates little more work is required to create something that is more easily reusable.

For this reason I created a Razor Hosting wrapper project that combines a bunch of this functionality into an easy to use hosting class, a hosting factory that can load the engine in a separate AppDomain and a couple of hosting containers that provided folder based and string based caching for templates for an easily embeddable and reusable engine with easy to use syntax.

If you just want the code and play with the samples and source go grab the latest code from the Subversion Repository at:

http://www.west-wind.com:8080/svn/articles/trunk/RazorHosting/

or a snapshot from:

http://www.west-wind.com/files/tools/RazorHosting.zip

Getting Started

Before I get into how hosting with Razor works, let’s take a look at how you can get up and running quickly with the wrapper classes provided. It only takes a few lines of code. Let’s start with a very simple template that displays some simple expressions, some code blocks and demonstrates rendering some data from contextual data that you pass to the template in the form of a ‘context’.

Here’s a simple Razor template:

@inherits RazorHosting.RazorTemplateBase
@using System.Reflection
Hello @Context.FirstName! Your entry was entered on: @Context.Entered

@{
    // Code block: Update the host Windows Form passed in through the context
    Context.WinForm.Text = "Hello World from Razor at " + DateTime.Now.ToString();
}

AppDomain Id:
    @AppDomain.CurrentDomain.FriendlyName
            
Assembly:
    @Assembly.GetExecutingAssembly().FullName

Code based output: 
@{
    // Write output with Response object from code
    string output = string.Empty;
    for (int i = 0; i < 10; i++)
    {
        output += i.ToString() + " ";
    }
    Response.Write(output);
}

Note: the @inherits above provides a way to override the default template type used by Razor and this wrapper. It’s optional, but it also provides Intellisense for your custom template in Visual Studio so it’s a good idea to provide the type in the template itself.

Pretty easy to see what’s going on here. The only unusual thing in this code is the Context object which is an arbitrary object I’m passing from the host to the template by way of the template base class. I’m also displaying the current AppDomain and the executing Assembly name so you can see how compiling and running a template actually loads up new assemblies. Also note that as part of my context I’m passing a reference to the current Windows Form down to the template and changing the title from within the script. It’s a silly example, but it demonstrates two-way communication between host and template and back which can be very powerful.

The easiest way to quickly render this template is to use the RazorEngine<TTemplateBase> class. The generic parameter specifies a template base class type that is used by Razor internally to generate the class it generates from a template. The default implementation provided in my RazorHosting wrapper is RazorTemplateBase.

Here’s a simple example that renders from a string and outputs a string:

var engine = new RazorEngine<RazorTemplateBase>();

// we can pass any object as context - here create a custom context
var context = new CustomContext()
{
    WinForm = this,
FirstName = "Rick",
Entered = DateTime.Now.AddDays(-10) }; string output = engine.RenderTemplate(this.txtSource.Text
new string[] { "System.Windows.Forms.dll" },
context); if (output == null) this.txtResult.Text = "*** ERROR:\r\n" + engine.ErrorMessage; else this.txtResult.Text = output;

Simple enough. This code renders a template from a string input and returns a result back as a string. It  creates a custom context and passes that to the template which can then access the Context’s properties. Note that anything passed as ‘context’ must be serializable (or MarshalByRefObject) – otherwise you get an exception when passing the reference over AppDomain boundaries (discussed later). Passing a context is optional, but is a key feature in being able to share data between the host application and the template. Note that we use the Context object to access FirstName, Entered and even the host Windows Form object which is used in the template to change the Window caption from within the script!

In the code above all the work happens in the RenderTemplate method which provide a variety of overloads to read and write to and from strings, files and TextReaders/Writers. Here’s another example that renders from a file input using a TextReader:

using (reader = new StreamReader("templates\\simple.csHtml", true))
{
    result = host.RenderTemplate(reader,  new string[] { "System.Windows.Forms.dll" }, 
this.CustomContext); }

RenderTemplate() is fairly high level and it handles loading of the runtime, compiling into an assembly and rendering of the template. If you want more control you can use the lower level methods to control each step of the way which is important for the HostContainers I’ll discuss later. Basically for those scenarios you want to separate out loading of the engine, compiling into an assembly and then rendering the template from the assembly. Why? So we can keep assemblies cached. In the code above a new assembly is created for each template rendered which is inefficient and uses up resources. Depending on the size of your templates and how often you fire them you can chew through memory very quickly.

This slighter lower level approach is only a couple of extra steps:

// we can pass any object as context - here create a custom context
var context = new CustomContext()
{
    WinForm = this,
FirstName = "Rick", Entered = DateTime.Now.AddDays(-10) }; var engine = new RazorEngine<RazorTemplateBase>(); string assId = null; using (StringReader reader = new StringReader(this.txtSource.Text)) { assId = engine.ParseAndCompileTemplate(new string[] { "System.Windows.Forms.dll" }, reader); } string output = engine.RenderTemplateFromAssembly(assId, context); if (output == null) this.txtResult.Text = "*** ERROR:\r\n" + engine.ErrorMessage; else this.txtResult.Text = output;

The difference here is that you can capture the assembly – or rather an Id to it – and potentially hold on to it to render again later assuming the template hasn’t changed. The HostContainers take advantage of this feature to cache the assemblies based on certain criteria like a filename and file time step or a string hash that if not change indicate that an assembly can be reused.

Note that ParseAndCompileTemplate returns an assembly Id rather than the assembly itself. This is done so that that the assembly always stays in the host’s AppDomain and is not passed across AppDomain boundaries which would cause load failures. We’ll talk more about this in a minute but for now just realize that assemblies references are stored in a list and are accessible by this ID to allow locating and re-executing of the assembly based on that id. Reuse of the assembly avoids recompilation overhead and creation of yet another assembly that loads into the current AppDomain.

You can play around with several different versions of the above code in the main sample form:

RazorSampleForm 

Posted in Razor  ASP.NET  .NET  

The Voices of Reason


 

Fabio Franzini
December 28, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Nice article Rick! I use it to for my project! Again, congratulations on the excellent idea!

Dennis Gorelik
December 28, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Rick,

It looks like monumental work.
I think the amount code that is required in order to kick off Razor outside of MVC is a bit too much.
In order to be useful in custom apps the calling call should look something like this:
string template = "My @razor @template";
string result = Razor.Render(template, "razor", "template"[, ... other parameters here ...]);

Thanks for trying it for us!

Rick Strahl
December 28, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Dennis with the wrapper I have it IS about that much code. It's about 3 lines of code.

The problem is that if you do it as a one liner you do not get the benefits of a hosted environment - caching, AppDomain hosting, additional assembly references etc. and so it takes a few extra lines (with the wrapper code).

A core engine has to have more flexibility and comparatively the native Razor engine is VERY easy to set up. Compared to ASP.NET runtime hosting, or VBA hosting this is practical trivial and it supports most of the options that one needs to build an effective scripting environment...

Donnie Hale
December 28, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

I think this is the longest article I've ever seen you post (in a single part, anyway). Are there advantages to using Razor over T4 (now that it's a first-class citizen within VS and for standalone applications)?

Rick Strahl
December 28, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Donnie - T4 is specific to Visual Studio so you need Visual Studio to use it which makes it useless for standalone applications. Also I've never been a fan of T4 since there isn't any decent editor support in VS even for it (you need third party tools to do that). Bottom line is if you want to generated code (or whatever) from within visual studio T4 is the easiest choice as the the environment will automatically pick up templates and convert them to output files. For external apps Razor is what you'd want.

Re: Long Article - yes, in the past I published these as articles, but it's just become easier to publish articles right here in the Blog rather than in a separate location. Easier to edit and maintain right here and more visible to the world on top of it. :-)

Travis
December 28, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Great article - thanks for sharing. I haven't needed custom templating in a many, many years, and way back then I was using StringBuilders. Can't wait to find an excuse to try this out :) P.S. Time to upgrade from svn to hg :)

Paul Cox
December 29, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Fantastic blog post, could almost be a chapter of a book!

I've been using Spark for email templating for a while but have wanted to switch to Razor to standardise the templating across MVC views and email views.

After pluging this in the main problem I ran into was that I couldn't get it to work with anonymous types even when not using a separate AppDomain. I'm not sure if it is possible to get this to work with a remote AppDomain but I've seen it working locally with the Razor templating engine on Codeplex.

Also I had a couple of questions/requests.

1. Is the engine thread-safe? I have a singleton RazorFolderHostContainer supplied by main IoC container that is called from my server app running MassTransit. As each message handler runs in a separate thread I could be accessing the engine at the same time across threads to generate 2 separate templates.

2. Are you planning to add support for Layout pages? Creating an email is pretty much the same as creating a web page so most of the functionality from the RazorViewEngine would be useful.

Oak Chantosa
December 29, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Your thoughts on any sort of integration with LINQPad ? http://linqpad.net/

Rick Strahl
December 29, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Paul - No the engine isn't thread safe at this point. I had no intention for this to work in a server environment since you can use the Razor engine natively with ASP.NET MVC. It's something to put on the list for the future possibly. Currently the issue is that compilation is relatively slow and the assembly caching will cause mutli-thread access issues. It should be perfectly possible however to run Razor multi-threaded with the right locking in place.

Haven't looked into layout pages yet - not quite sure how to implement this inside out partial rendering - I think that would require pre-parsing of the page to get the 'master' then rendering that and rendering the current page as a partial. Again something to think about for the furture. For now the FolderHostContainer supports partial rendering to provide for at least some minimal composition functionality.

Rick Strahl
December 29, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Oak - what kind of integration are you thinking of? LinqPad already has good execution options and the .Dump() command works well for output of values. I'm not sure what a scripting engine would add? Using a script instead of a codeblock?

0xfeeddeadbeef
December 30, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

There is also much less versatile implementation of this idea: http://razorengine.codeplex.com/

Duane Craw
December 30, 2010

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Great stuff! I was playing around with this, and found a way to get intellisense on the Context object.

Add:
@using mynamespase
@{MyObject Model = Context}
   ....content... @Model.MyField ...content


This makes editing the .cshtml template much easier!

Takashi,Takehara
January 01, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Great atricle!

I wrote sample program RazorHosting, but I can not call System.Web.Mvc.HtmlHelper.
New base class have HtmlHelper Html property. It coding referring to Razor Templating Engine.
Why I has MethodAccessException.
System.ComponentModel.DataAnnotations APTCA assembly...

something a good idea?

Steve
January 01, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Great post Rick. I've used NVelocity to do this work for several years now. It will be interesting to try this out using Razor. Thanks to your write-up it might be something less painful to try on the first attempt :)

Thanks Again! Happy New Year!

Rick Strahl
January 01, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Takashi - MVC Helpers aren't available by default. You're not running MVC when you're hosting Razor yourself so you don't get all the same assembly includes nor the same feature set you get when using the hosting engine on its own. I think the MVC helpers have all sorts of dependencies that will not make them easily useable in a self hosted environment.

However, ASP.NET Web Pages (which is the package that Razor comes in) includes System.Web.Helpers.dll that provides some similar functionality and that isn't tied closely to the MVC engine but to the standalone Razor sytnax use in WebMatrix.

Eber Irigoyen
January 02, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

"Razor is an alternative view engine that can be used instead of the ASP.NET Page engine that currently works with ASP.NET WebForms and MVC."
It *can be used in WebForms?

Rick Strahl
January 02, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Eber - yes you can you can use Razor files inside of an ASP.NET WebForms site. Simply add the .csHtml or .vbHtml files into the project and then call the new files without the .csHtml extension (they are auto-routed so /mysite/Default.csHtml becomes /mysite/Default). This is assuming you installed MVC 3 or WebMatrix to get Razor and the VS tooling onto the machine in the first place.

What you can't do is mix and match Razor and ASP.NET WebForms syntax within a single page (other than embedding content from an iFrame or AJAX load operations).

Eber Irigoyen
January 04, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

oh ok, that's what I thought, just wanted to make sure you didn't have another one under your sleeve

Anders Lybecker
January 04, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Great stuff.

I’m trying to reference the current assembly, the one that uses the RazorEngine, without success. I’m have implemented helper template methods, sort of like regular MVC HtmlHelpers, but they all reside inside the same assembly.

I prefer not using the CustomContext, as it for data, but it’s my current workaround. I’m not able to user partial render, as the current implementation is only file based. My template views are embedded resources.

Any suggestions?

Rick Strahl
January 04, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Anders - How are you trying to access the helper assembly? Make sure you add the ReferencedAssemblies in startup as well as ReferencedNamespaces (in the latest build in SVN).

You can also create custom templates now with RazorFolderHostContainer<T> and then override InitializeTemplate(context,config) to pass in the context and break it out into strongly typed objects.

If you have problems of things that are failing more specific info will help. You can post that here (which is a better forum for interactive discussions):
http://www.west-wind.com/wwThreads/default.asp?Forum=West+Wind+Web+Toolkit+for+ASP.NET

# TWC9: WebMatrix, MVC, Orchard, NuGet Gallery

Don’t have Silverlight? Download it here ! Or, download the video as a high quality WMV , medium quality

Blog J.Schweiss
February 12, 2011

# Hosting the Razor Engine for Templating in Non-Web Applications

Hosting the Razor Engine for Templating in Non-Web Applications

David Klebanoff
March 11, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hello Rick,
Great article and software. Exactly what I'm loolking for. But one thing, you have not clarified the license for RazorHosting. What is it?

sri
June 17, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hello Rick,

Very dense and useful article. I am trying to achieve the same and your article helped me a lot.
However I am facing am issue when I refer an assembly "MyAssembly.dll".

I have created a Class Library project "RazorBuilder" which exposes method to return the html output of the razor.

I have added the reference assembly as below
result = host.RenderTemplate(reader,
new string[] { "System.Web.dll","MyAssembly.dll" },
Context);

This works fine when I run the test methods in the Test Project by referencing the "RazorBuilder".

The same does not work when i move the "RazorBuilder" into the Windows Service . Gives the error "Error: Metadata file 'MyAssembly.dll' could not be found"

Can you help me out on this.
Thanks in advance.
But When i move the same to Windows Service , it fails with an error:
Line: 0\t Col: 0\t Error: Metadata file 'MyAssembly.dll' could not be found .

Could you please help me out on this.
Thanks in advance.

elmar
June 27, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Dear Rick,

This contribution is great. Thanks for that. Question though: the RenderTemplate keeps returning null and the errormessage says it can't find a referenced assembly. When I supply the full path to the assembly, the template engine still complains about unable to find assembly. But it is there.

Any ideas?

Thanks

Elmar

Rick Strahl
June 27, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Elmar - assemblies should be in the host application's assembly path (ie. startup folder or privateBin folder). If necessary you can adjust the bin path with startup/config file settings to accomodate additional folders.

Additionally if you use in process hosting you should be able to just load the assembly into the current process and it should just work since in that case the AppDomain and all assembly loads are shared.

Pranas
June 28, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Great article, Rick.
Is there a possibility to debug cshtml file?

Rick Strahl
June 28, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Pranas - yes. If you have Visual Studio 2010 with the Razor extensions installed just set a breakpoint in a code block anywhere and the debugger will pop.

Pranas
June 29, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

What is "Razor Extensions"? Is it part of ASP.NET MVC3 install?
I can debug cshtml files in ASP.NET MVC application, but I can't debug cshtml files in your sample application.
I tried to change your RazorEngine to generate .pdb file together with .dll from .cshtml file. And when this generated assembly is loaded to AppDomain, I can see that symbol file is also loaded. The generated .cs file from .cshtml is also in temp directory, but I don't understand how debugger finds its .cs and .cshtml file from .pdb.
Do you know anything about this?
Have you tried to use debugger in your sample application to debug .cshtml file?

Rick Strahl
June 29, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Pranas - I think you have to install the Razor Syntax color highlighting for Visual Studio 2010 separately from the Component Gallery to get syntax coloring and editor mapping so you can set a breakpoint in the VS editor. Otherwise you have to set a manual breakpoint with System.Diagnostics.Debugger.Break().

Scott Holodak
July 05, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Have you seen David Ebbo's post on pre-compiling Razor views?
http://blog.davidebbo.com/2011/06/precompile-your-mvc-views-using.html

It would be very cool if compiled Razor views could live in a project with no web/MVC dependencies and the binary Razor views in that .dll could be leveraged by both MVC apps and standalone applications/services. I could see some very interesting use cases.

For instance, we have a need to present a very large HTML-based form both online and in a WinForms WebBrowser control. We would like to leverage the same exact templates for both, without having to have .cshtml files editable by the user on the filesystem in the WinForms app.

Any idea if it would be possible to pre-populate the page/partial page cache for high-volume sites. You could render the templates into the cache and eliminate the user waiting on the cache miss. This could be useful for sites that perform overnight calculations or even posts to high-volume forums/threads/blog comments.

Rick Strahl
July 05, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Scott - the code has no MVC/Web dependencies now. Other than VS editing support the razor libraries are self contained and fully portable without any pre-installed libraries.

Pre-compilation is not that important IMHO - the whole point of a scripting engine should be that it is dynamic. The code I provide can actually write out the compiled assemblies and be reused although I think that's not all that useful. Compilation is pretty fast (unlike the full ASP.NET runtime which was super resource extensive) and pages are cached afterwards if you use one of the host containers I talk about in the article.

Scott Holodak
July 05, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

The benefit of the compiled views here would be that the MVC app and the WinForms/WPF app would be referring to the same exact views in the win/web version. So if we make changes to the views (or the underlying models), the MVC app and the WinForms/WPF app would both inherit the changes and would render the same HTML.

I'll try and mash these two solutions together and post back if I have any luck.

MvcApp.sln
- Shared\Data.csproj
- Shared\Models.csproj
- Shared\RazorViews.csproj
- MvcApp.csproj

WinFormsOrWpfApp.sln
- Shared\Data.csproj
- Shared\Models.csproj
- Shared\RazorViews.csproj
- WinFormsOrWpfApp.csproj

Harry
July 05, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

I am using the RenderPartial in the razor view and getting an error :Line: 399\t Col: 28\t Error: The name 'RenderPartial' does not exist in the current context.

What am I missing here?

Rick Strahl
July 06, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Harry - are you using the FolderHostContainer? RenderPartial() is not built into Razor natively so it requires a custom template and the mechanism to look up a partial path. The implementation for this is in the FolderHostContainer and the associated custom template.

Paul Perrick
December 10, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

First of all, thank you for writing this library! It is exactly what I was looking for and I don't have to write anything similar myself.

Please please please host it on a site like github so a forum is included. From what I can see in the above comments, people are using the library (or want to use it) and if there are questions, it is difficult to organize questions/answers in the comments of a blog.

Thank You,

Paul

Rick Strahl
December 10, 2011

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Paul - you can post questions and comments for forum discussion here:

http://www.west-wind.com/wwthreads/default.asp?forum=West%20Wind%20.NET%20Tools%20and%20Demos

Andrew
February 13, 2012

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hi Rick, excellent stuff, i'm using a slightly modified version as an email templating engine and it works really well. Ironically, the email templating engine is used in a domain dll which is referenced by an MVC app ;)
And that is also the source of a problem: when i call Start() on a RazorStringHostContainer, and the RazorEngineFactory tries to CreateRazorHostInAppDomain(), which in turn calls GetRazorHostInAppDomain() i get an error on this line:

host = (RazorEngine<TBaseTemplateType>) LocalAppDomain.CreateInstanceFrom(AssemblyPath, typeof(RazorEngine<TBaseTemplateType>).FullName).Unwrap();

the error i get is "Could not load the file or assembly....The system cannot find the file specified"

Now i suspect this has something to do with ASP.Net's permissions, but i'm not sure how to solve it. Any ideas?

Greg Brant
February 20, 2012

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Rick,

I'm getting an error when calling RenderTemplate();

The error is "Couldn't activate type instance: _RazorHost._40a86a00_8583_4bae_8fda_9a82b8866f96"

My call site looks like this

var engine = new RazorEngine<LineTemplate>();
var renderOutput = engine.RenderTemplate(template, new string[] { "Transformer.exe" }, item);

Transformer.exe is my console application when LineTemplate is declared.

Aviad Pineles
March 17, 2012

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hi, there's a missing feature when using strongly typed models, the Model property doesn't get automatically assigned with the context object.

I've created a small patch to fix it, here it is:

Index: RazorEngine.cs
===================================================================
--- RazorEngine.cs    (revision 62)
+++ RazorEngine.cs    (working copy)
@@ -554,6 +554,15 @@
         {
             try
             {
+                var templateType = instance.GetType();
+                var templateModelProperty = templateType.GetProperty("Model", BindingFlags.Instance | BindingFlags.Public);
+                if (templateModelProperty != null)
+                {
+                    var contextModelType = context.GetType();
+                    var templateModelType = templateModelProperty.PropertyType;
+                    if (templateModelType.IsAssignableFrom(contextModelType))
+                        templateModelProperty.SetValue(instance, context, null);
+                }
                 instance.Context = context;
                 instance.Execute();
             }

Rick Strahl
March 18, 2012

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hi Avid,

Great comment. I modded this using dynamic code with a plain assignment and exception trapping for the missing model property:

instance.Context = context;
                
if (context != null)
{
    // if there's a model property try to 
    // assign it from context
    try
    {
        dynamic dynInstance = instance;
        dynInstance.Model = context;
    }
    catch {}                    
}
 
instance.Execute();

Ed
May 04, 2012

# re: Hosting the Razor Engine for Templating in Non-Web Applications

I missed referencing an assembly and RazorFolderHostContainer (Rev 64) failed at line 311 with the following error when trying to access Engine.ErrorMessage:

Cannot obtain fields or call methods on the instance of type 'RazorHosting.RazorEngine<RazorHosting.RazorTemplateFolderHost>' because it is a proxy to a remote object.

assemblyId = Engine.ParseAndCompileTemplate(this.ReferencedAssemblies.ToArray(), reader);
 
// need to ensure reader is closed
if (reader != null)
    reader.Close();
 
if (assemblyId == null)
{
    this.SetError(Engine.ErrorMessage);
    return null;
}

James
May 31, 2012

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hi Rick, Just a quick note to say thanks for the code.
I too was experiencing the same issue as Richard when running this in a web environment, when trying to use host containers in a new AppDomain returning FileNotFoundException.

In order to resolve this I modified your code slightly in the CreateDomain function of the RazorFactory class.

Evidence evidence = AppDomain.CurrentDomain.Evidence;
setup = AppDomain.CurrentDomain.SetupInformation;

Now, I don't get any FileNotFoundException and it was just a matter of ensuring my complex object that I use as my context was Serializable, and voila !!! We're in business!

Hope this helps someone.

Thanks again, off to Maui this summer so very much looking forward to that

Cameron Elliot
August 31, 2012

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Thanks very much for this code, it has been very helpful. I think it may need a little HtmlEncoding though:

                //HttpEncode so that anything written out with reserved characters is escaped
        public virtual void Write(object value)
        {
            Response.Write(HttpUtility.HtmlEncode(value));
        }
 
        //HttpEncode so that anything written out with reserved characters is escaped
        public virtual void WriteString(object value)
        {
            Response.Write(HttpUtility.HtmlEncode(value));
            Response.Write(Environment.NewLine);
        }
 
        //No HttpEncode here as we are writing literal content
        public virtual void WriteLiteral(object value)
        {
            Response.Write(value);
        }

Frank Schwengsbier
October 29, 2012

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hi Rick,

great work! Do I understand it right? This can be a solution to run custom c# code on-the-fly? Like the:
- someone edits the x.prg during the runtime of a FoxPro App
- compile x.prg
- do x
commands in FoxPro?

Regards
Frank

Rick Strahl
October 30, 2012

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Frank - sort of. It's more of a templating mechanism that can also run code rather than a 'scripting' engine. But in theory you can create a template that only runs code. Using the ResultData property of the template and the engine properties in my implementations you can even pass data results back.

Martin
April 12, 2013

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Thanks, Very useful.

I had an issue where stopping the Host wasn't unloading the AppDomain successfully though.

Changing a line in the RazorBaseHostContainer Stop() method to
 RazorEngineFactory<TBaseTemplateType>.UnloadRazorHostInAppDomain();
Seems to have fixed it.

BTW: Is there any technical reason for not allowing Exceptions to bubble up or is this just a design decision?

Alexander Gurevich
October 30, 2013

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hi,
Great post!. Thank you very much.
I am trying to use System.Web.HtmlString. For example:

@{var expression = new System.Web.HtmlString("<b>Hello</b>");}
<p>
@expression
</p>

I am getting error:
Line 90 Col 36: The type or namespace name 'HtmlString' does not exist in the namespace 'System.Web' (are you missing an assembly reference?)
System.Web is referenced and I see in generated file namespace
========================
namespace @__RazorHost {
using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Web;
========================

Any idea what am I missing?

Thank you.

Rick Strahl
October 30, 2013

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Alexander - are you sure you added a REFERENCE to System.Web?

Alexander Gurevich
October 31, 2013

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Rick Strahl
Yes, I confirmed it. System.Web is in references of my project.
The weird part is the error says "'HtmlString' does not exist in the namespace 'System.Web'".
I use full name that includes namespace...

Is there another way to use html string in c#?

Rick Strahl
October 31, 2013

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Maybe you're ending up loading the wrong version? You're using the fully qualified assembly name to specify the System.Web instance?

Alexander Gurevich
October 31, 2013

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Rick Strahl
I do not understand your question.

The project is referenced. System.Web properties:
Version: 4.0.0.0
Runtime Version: v4.0.30319

In Razorengine constructor I added
ReferencedNamespaces.Add("System.Web");

The error shows me that reference is there
namespace @__RazorHost {
    using System;
    using System.Text;
    using System.Collections.Generic;
    using System.Linq;
    using System.IO;
    using System.Web;


Where I should have used fully qualified assembly name to specify the System.Web?

Thank you.

Rick Strahl
October 31, 2013

# re: Hosting the Razor Engine for Templating in Non-Web Applications

You need to add the assembly in addition to the namespace and that's regardless of whether the assembly is being referenced in your host application. The Razor instance (especially if you are using the separate AppDomain option) requires its own set of assembly references. So make sure you have

host.AddAssembly("System.Web.dll")

Martin Kral
January 17, 2014

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hi.
I using this solution for render some special HTML document without MVC.
It's very useful !
But I have problem, if i try render @for (int i = 0; i < Context.Competitors.Count(); i++)
{
....
where is Competitors some List<Collection>.
I get this error:
Non-invocable member 'System.Collections.Generic.List<EventCompetitor>.Count' cannot be used like a method.

If i have some variable in the Context, where i write Count directly, it's proccessed without error. Where is the problem?
Thank you.

Hugo Logmans
May 16, 2014

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hi Rick,

first: thanks for your great code and explanation.

But I run into a issue: if I compile using "CompileToMemory = true", the following situation does not work. If I set it to false, it all works fine.

Situation: the calling application resides in a "program files" subfolder (vfp9.exe). It calls an in process ActiveX dll (which resides in my dev folder). Dependent assemblies that are not found are redirected to the dev folder. In this case, the generated assembly (which uses the assembly that generates it), cannot load the calling assembly as a dependency. The failure is when compiling the template (that succeeds, but internally a loaderror occurs that seems to be suppressed). I tried some things to provide paths to dependent assemblies, but nothing seems to work when the compiletomemory feature is active...

Maybe you have any clue?

Rick Strahl
May 16, 2014

# re: Hosting the Razor Engine for Templating in Non-Web Applications

@Hugo - you probably have a missing assembly somewhere. All dependencies must be resolvable through the bin path. Generally in memory should work with any assembly the top level .NET AppDomain can access (ie. whatever you loaded from FoxPro).

Iaacov
April 12, 2015

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hi Rick,

First, thanks for this great tool.

We successfully use the RazorHosting wrapper inside a Windows Service that delivers template-based emails.

The host container is started in the service OnStart event and stopped in OnStop.
The template runs in a separate AppDomain.

The problem arises when the service stays idle for more than 5 minutes. In this case the template's lifetime expires and doesn't compile any new code.

The RazorBaseHostContainer class has an InitializeLifetimeService method which, according to documentation, appears to exists for this purpose but I couldn't figure out how to use it.

Can you please explain me how to solve this problem?

Renato
April 20, 2015

# re: Hosting the Razor Engine for Templating in Non-Web Applications

Hi Rick,

thank you for the great post. Is there any way you can think of to get "Razor Intellisense" working at runtime in an editor? Something like your WinForm example with integrated Razor Intellisense in the "Template to Render" textbox.
 

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2017