EPiServer  /  CMS April 29, 2010

Dependency Injection with Page Type Builder 1.2

The latest release of Page Type Builder brings a couple of interesting changes when it comes to testability and how we can design our applications. You are now able to inject dependencies into instances of your own page type classes using either the IoC container StructureMap or a custom implementation. Before we go into any more detail, let’s look at a simple scenario.

As you may know, when you use Page Type Builder and you ask DataFactory for a page Page Type Builder will intervene and replace the PageData object that DataFactory is about to return with an instance of a class that you have created. That is, given that you have created a class named Article and you have a valid PageReference for such a page you can do stuff like this:

Article article = 
    (Article) DataFactory.Instance.GetPage(pageLink);

//or this

IEnumerable<Article> articles = 
    DataFactory.Instance.GetChildren(pageLink).OfType<Article>();

This in turn of course enables strongly typed access to properties. But, as you are no longer working with plain old PageData objects but instances of classes that you have created, you are also able to extend what used to be regular PageData objects with methods. That means that pages can be first class citizens in something resembling a domain model. Let’s for instance say that visitors are able to comment on articles and that comments are saved as instances of another class and page type, Comment, which are added as children to the article being commented on. You can then add a method to the Article class to retrieve comments:

public IEnumerable<Comment> GetComments()
{
    return DataFactory.Instance.GetChildren(PageLink).OfType<Comment>();
}

The above method is a bit too simple as it doesn’t check access rights and publish status for the returned comments, something you can do with the FilterForVisitor class, but you get my point. Using this approach we can take code in our pages, user controls etc that looks like this

aRepeater.DataSource = 
    DataFactory.Instance.GetChildren(CurrentPage.PageLink)
    .OfType<Comment>();

And move it into our page type classes and thereby make our presentation layer, which with Web Forms is very hard to test, contain less logic.

aRepeater.DataSource = CurrentPage.GetComments();

Dependency Injection

There’s one problem with this approach though. When the code is dependent on other components we need to isolate our code from them in order to be able to create unit tests (for a broader discussion about isolating code check out my introduction to Inversion of Control).

That is the GetComments method in our scenario relies on DataFactory.Instance which in turn relies on EPiServer having been started. That in turn means that we can’t write unit tests for our code unless we first change it to rely on an abstraction of DataFactory instead and find a way to change which instance of that which is used. Usually the recommended solution for this is to use dependency injection. That is we create a constructor with which we supply an implementation of the abstract component (IPageSource in this case) that our code depends on and change our code to use that instance, like this:

private IPageSource dataFactory;

public Article(IPageSource dataFactory)
{
    this.dataFactory = dataFactory;
}

public IEnumerable<Comment> GetComments()
{
    //Now uses dataFactory instead of
    //DataFactory.Instance
    return dataFactory.GetChildren(PageLink)
        .OfType<Comment>();
}

In our case though the instances of our class, the articles, are instantiated by Page Type Builder who doesn’t know how to use that constructor as it doesn’t know what IPageSource, or whatever the types of your dependencies may be, is and how to get it’s hands on one.

Until now that is.

Changes in version 1.2

This will get a bit technical. Feel free to skip to the next headline if you get bored.

Page Type Builder has a class named PageTypeResolver which has a method named ConvertToTyped which it uses when it replaces the PageData objects that DataFactory returns. This in turns uses an instance of a class named TypedPageActivator which it accesses through a property named Activator. That is when Page Type Builder intervenes and replaces a returned PageData object from DataFactory it will use PageTypeResolver.Instance.ConvertToTyped() to create the object that DataFactory will return. ConvertToTyped will in turn delegate to PageTypeResolver.Instance.Activator. This property has been made public in version 1.2 and so has the relevant method in TypedPageActivator, CreateInstance, which has also been made virtual. This means that we can create a subclass of TypedPageActivator and tell Page Type Builder (or rather the PageTypeResolver) to use that instead, giving us full control over how PageData objects are instantiated.

Resolve dependencies using StructureMap

By creating our own subclass of TypedPageActivator we can use whatever we want to resolve dependencies. However Page Type Builder ships with one ready-to-use implementation named StructureMapTypedPageActivator, which uses StructureMap. You can also quite easily create your own Activator to use your favorite IoC container.

Using StructureMapTypedPageActivator is easy, just create a container (or use the static one in StructureMap’s ObjectFactory), create an instance of StructureMapTypedPageActivator and tell PageTypeBuilder to use it in the Application_Start method in global.asax.

protected void Application_Start(Object sender, EventArgs e)
{
    Container container = new Container();
    PageTypeResolver.Instance.Activator = new StructureMapTypedPageActivator(container);
}

Of course that won’t do you much good unless you configure the container. There are many ways of doing that. Below is one simple example.

protected void Application_Start(Object sender, EventArgs e)
{
    Container container = new Container();
    container.Configure(c =>
        c.For<IPageSource>().Use(DataFactory.Instance));
    PageTypeResolver.Instance.Activator = new StructureMapTypedPageActivator(container);
}

Are my ramblings above not making any sense? Do you like or hate these new features? Would you like to see support for any particular other IoC container shipped with Page Type Builder? Do you want to express your love for motorized chariot racing? Write a comment!

PS. For updates about new posts, sites I find useful and the occasional rant you can follow me on Twitter. You are also most welcome to subscribe to the RSS-feed.

Joel Abrahamsson

Joel Abrahamsson

I'm a passionate web developer and systems architect living in Stockholm, Sweden. I work as CTO for a large media site and enjoy developing with all technologies, especially .NET, Node.js, and ElasticSearch. Read more

Comments

comments powered by Disqus

My book

Want a structured way to learn EPiServer 7 development? Check out my book on Leanpub!

More about EPiServer CMS