EPiServer  /  CMS August 04, 2009

EPiServer and MVC – Retrieving current page using a custom model binder

In a previous post I described my first attempt at working with EPiServer and MVC along with Page Type Builder. In that solution all controllers inherited from a base class which had an instance of a class called CurrentPageResolver. Each controller action method received the URL segment (or slug) for a page and then used the CurrentPageResolver to retrieve the corresponding PageData object.

This method has a serious flaw in that it makes the controller do more than it should, and what’s even worse, it renders the actions almost impossible to unit test as the CurrentPageResolver in turn will fetch the PageData object from DataFactory and in the end the database.

A much cleaner solution, which makes the controllers highly testable, is to let each action method have a currentPage parameter of type PageData (or in my case BasePageData). This is easily done by creating a custom model binder.

The first thing we should do, compared to the solution I described in the previous post, is to modify the controllers. Below is an example, my ArticleController. It no longer inherits from ControllerBase (which I removed from the project) and the actions have a currentPage parameter.

public class ArticleController : Controller
{
    public ActionResult Index(BasePageData currentPage)
    {
        return View(currentPage);
    }

    public ActionResult Edit(BasePageData currentPage)
    {
        return View(currentPage);
    }
}

Secondly, we should create a custom model binder by creating a new class that implements IModelBinder.

public class PageDataModelBinder : IModelBinder
{
    public PageDataModelBinder()
    {
        CurrentPageResolver = new CurrentPageResolver();
    }

    private CurrentPageResolver CurrentPageResolver  { get; set; }

    public object BindModel(ControllerContext controllerContext, 
                                ModelBindingContext bindingContext)
    {
        string slug = (string) controllerContext.RouteData.Values["slug"];
        return CurrentPageResolver.GetCurrentPage(slug);
    }
}

When asked for a model object our model binder retrieves the slug part of the route and asks an instance of CurrentPageResolver for the corresponding PageData object.

The last step is to register our model binder as responsible for delivering objects of type BasePageData (which is the base type for all of my PageData objects). This is easily done in global.asax.

protected void Application_Start(Object sender, EventArgs e)
{
    ModelBinders.Binders.Add(typeof(BasePageData), new PageDataModelBinder());
}

Source code

I’ve zipped the relevant parts of my project and put them up for download here.

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