Dependency Injection

With the release of v2 of the RazorEngine templating framework, we’ve introduced support for Dependency Injection in your custom base templates. This can be enormously beneficial for those wanting to introduce additional functionality, which we want to decouple from out template code. In this example, I’m going to walk you through an example of DI with RazorEngine using Microsoft’s Unity IoC container.

Base Template Design

To take advantage of DI, there is a specific design requirement around the constructor you use for your template base. It is normally recommended to mark your construcors as protected. Unfortunately to ensure that DI works in restricted environments (like ASP.NET Medium trust), we can’t rely on BindingFlags.NonPublic to grab any non-public constructors.

Let’s look at a custom base template:

/// <summary>
/// Defines a custom template base.
/// </summary>
/// <typeparam name="T">The model type.</typeparam>
public abstract class CustomTemplateBase<T> : TemplateBase<T>
{
    #region Constructor
    /// <summary>
    /// Initialises a new instance of <see cref="CustomTemplateBase{T}"/>.
    /// </summary>
    /// <param name="formatter">The formatter to use.</param>
    public CustomTemplateBase(IFormatter formatter)
    {
        if (formatter == null)
            throw new ArgumentNullException("formatter");

        Formatter = formatter;
    }
    #endregion

    #region Properties
    /// <summary>
    /// Gets the formatter used to format objects.
    /// </summary>
    public IFormatter Formatter { get; private set; }
    #endregion
}

As part of the code generation in v2, any public constructors are copied into the generated class, wrapping the arguments with calls to the base class. So in our generated class, we’d have something such as:

public class abcdefghijk : RazorEngine.Demo.Templates.CustomTemplateBase<dynamic>
{
  public abcdefghijk(RazorEngine.Demo.Services.IFormatter formatter)
    : base(formatter)
  {
  
  }
}

Because we’ve now introduced a public non-empty constructor, it allows us to inject instances into our template instances. So, how do we do that?

Activators

A new contract in v2 is the IActivator, it is defined as:

/// <summary>
/// Defines the required contract for implementing an object activator.
/// </summary>
public interface IActivator
{
    #region Methods
    /// <summary>
    /// Creates an instance of the specified type.
    /// </summary>
    /// <param name="type">The type to create an instance.</param>
    /// <returns>The <see cref="ITemplate"/> instance.</returns>
    ITemplate CreateInstance(Type type);
    #endregion
}

It does exactly what it is named. It activates instances of a type. With this simple contract in place, we can wire up a custom activator which RazorEngine will use to create an instance of the template. We’ve also added support for your activators to be expressed as Func<Type, ITemplate> instances, which are wrapped in an internal DelegateActivator instance, which implements our contract. Now let’s wire up our container and sample types:

// Create the container and register our sample types.
var container = new UnityContainer();
container.RegisterType(typeof(IFormatter), typeof(UppercaseFormatter));

// Set an activator which will initialise our template.
Razor.SetActivator(t => (ITemplate)container.Resolve(t));

// Set our custom base type.
Razor.SetTemplateBase(typeof(CustomTemplateBase<>));

Unity makes it relatively easy to build types that haven’t previously been registered. In our example above, we’ve defined a sample service, the IFormatter and a sample implementation the UppercaseFormatter. When we call out template:

// Test out custom base template.
string template = "Normal Text: @Model.Textn Uppercase: @Formatter.Format(Model.Text)";
string result = Razor.Parse(template, new { Text = "Sample Text" });

…we get the result:

Normal Text: Sample Text
Uppercase: SAMPLE TEXT

I hope this gives you a good introduction to how we’ve enable Dependency Injection support in RazorEngine. I’ve attached the sample project, enjoy.

Download VS2010 Project

Digg This
Reddit This
Stumble Now!
Buzz This
Vote on DZone
Share on Facebook
Bookmark this on Delicious
Kick It on DotNetKicks.com
Shout it
Share on LinkedIn
Bookmark this on Technorati
Post on Twitter
Google Buzz (aka. Google Reader)