RazorEngine v2

What’s new in RazorEngine v2?
RazorEngine v1.2 has had some great feedback and certainly allowed us a good architecture to build from. It was a little too complex to configure different language providers, and of course we have the old BOME issue which I know a lot of people would like to see solved. Over the past few days I’ve been rewriting the project from scratch, taking advantage of the RTM release of Razor (via MVC3/Web pages), as well as hopefully finding a way of tackling Medium trust. Let’s take a look…

Architecture
In v1.2 we relied on an interface called the ILanguageProvider which generated our CodeDomProvider and the RazorCodeLanguage used in parsing. To address our rethink for ASP.NET Medium compilation scenarios, the ILanguageProvider was counter productive, it brought about an immediate security barrier due to exposing a CodeDomProvider instance.

To change this and to simplify the interface, we’ve dropped the ILanguageProvider in favour of an ICompilerService contract, here’s it’s definition:

public interface ICompilerService
{
string BuildTypeName(Type templateType, Type modelType);
Type CompileType(TypeContext context);
}
Our TemplateService is now built around this contract, completely abstracting the service from any knowledge of language providers. This also allows us to implement a specific compiler service for web scenarios (we’ll come onto that in a bit).

Also, we’ve had a look at improving the configuration mechanism. Historically the ILanguageProvider specified the language to be used in parsing, with the idea that if future RazorCodeLanguage instances are created for languages other that C# and VB, we could easily add support in with (hopefully) no change. While this is still possible, we’ve changed the configuration to support a leaner Language enum, which currently has two members, C# and VisualBasic. Combining this with a new ICompilerServiceFactory contract:

public interface ICompilerServiceFactory
{
ICompilerService CreateCompilerService(Language langauge, bool strictMode, MarkupParser markupParser);
}
We now have an easy way of generating instances of our ICompilerService instances. In the core library we have two implementations, CSharpDirectCompilerService and VBDirectCompilerService. I’ve used the ‘Direct’ terminology to reflect that they call and compile the types directly using the CodeDomProvider.

ASP.NET Medium Trust
Onto the medium trust issue! With our new architecture in place, we can create a web specific set of compiler services and a factory to produce them. In Medium trust scenarios we can’t instantiate an instance of a CodeDomProvider directly, as we don’t have the appropriate permissions to do so. But, the ASP.NET build provider mechanism can be used to this advantage. Our web-specific compiler service implementations make a call ASP.NET’s BuildManager type. To make this call we had to use a virtual path, which has the knock on effect of having to build a simple virtual path provider also. So that’s what we’ve done. To use RazorEngine in medium trust, we have to do some configuration first:



Let’s not forget to wire up our virtual path provider:

protected void Application_Start()
{
HostingEnvironment.RegisterVirtualPathProvider(new RazorVirtualPathProvider());
}
I know what you’re thinking, too much configuration, but there isn’t a lot we can do about that I’m afraid! The good news is, now that is all configured, we can now use Razor templates in medium trust, and I would say the extra work done is well worth it.

Dependency Injection
On a whim I was considering how we could possible allow you to introduce dependency injection in your templates. This was always tricky because we had to control how templates we generated. To allow you to control how the template was instantiated, we’ve introduced a new interface, the IActivator:

public interface IActivator
{
ITemplate CreateInstance(Type type);
}
Our default implementation, the DefaultActivator simply makes a call to Activator.CreateInstance, which is good enough for simple templates. If you are introducing dependencies, this won’t do. Using Unity as an example IoC container, you can now do the following:

public abstract class InjectableTemplateBase : TemplateBase
{
public InjectableTemplateBase(ISomething something)
{
Something = something;
}

public ISomething Something { get; private set; }
}
With out demo base template, let’s configure an example:

var container = new UnityContainer();
container.Register(typeof(ISomething), typeof(HelloWorldSomething));

Razor.SetActivator(t => container.Resolve(t) as ITemplate);
Razor.SetTemplateBase(typeof(InjectableTemplateBase<>));

string result = Razor.Parse(“Hello @Something.DoSomething() my name is @Model.Name”, new { Name = “Matt” });
You’ll notice we’re using lambda to set our activator, we’ve added a DelegateActivator which consumes a Func instance, making it much more fluent. Activators can also be configured.

All of these new features are handled through the TemplateService, so if you want to create separate instances of the TemplateService to handle different base types, different activators, different languages, you can.

What’s next?
The code isn’t currently on the CodePlex project site, I want to have a chance to refine and refactor any small changes that might be required, and then it’ll be submitted. This blog post is just to wet your appetite and to let you know where we are heading. Thanks all to using our RazorEngine framework, and we’re glad you like it!

Leave a Reply

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

Related Post