Copyright Johan Kronberg 2009-2024 Latest posts RSS feed X: johankronberg LinkedIn Profile GitHub: krompaco
Site generated by: Record Collector

A ViewModelBuilder pattern for Episerver with MVC

An often discussed topic is how to set up a project's view models, content types and layout.

Most projects usually end up with one of the three patterns described way back in Steve Michelotti's ASP.NET MVC View Model Patterns.

In that post I took a liking to the ViewModelBuilder approach and decided to try it out for an Episerver CMS site.

View model structure

There are no type parameters and nothing is going on in any constructor. If you feel like it you can keep them clean of any CMS content type coupling as well which makes it easier to setup for example Commerce products in the same layout but with a different builder class. Properties removed for brevity.

public class LayoutViewModel
{
    public string Title { get; set; }
}

public class StandardPageViewModel : LayoutViewModel
{
    public string PublishedSince { get; set; }

    // If CurrentPage makes your life easier you put it here
    public StandardPage CurrentPage { get; set; }
}

The builder class

This one looks great to me. It does all the work and fetching and makes it possible for me to choose which work to do for the specific controller and view. It takes CurrentPage only to work with it and doesn't set it on some property by itself.

public class LayoutViewModelBuilder<TViewModel, TPageType>
        where TViewModel : LayoutViewModel, new()
        where TPageType : SitePageData
{
    private readonly TPageType currentPage;

    private readonly TViewModel vm;

    public LayoutViewModelBuilder(TPageType currentPage)
    {
        this.currentPage = currentPage;
        this.vm = new TViewModel();
    }

    public LayoutViewModelBuilder<TViewModel, TPageType> WithMeta()
    {
        this.vm.Title = SomeLogicSomewhere(this.currentPage);
        return this;
    }

    public LayoutViewModelBuilder<TViewModel, TPageType> WithBreadcrumbs()
    {
        // Fetch data for breadcrumbs and set props needed by breadcrumbs display
        this.vm.BreadcrumbList = SomeLogicSomewhere(this.currentPage);
        return this;
    }

    public LayoutViewModelBuilder<TViewModel, TPageType> WithSubNavigation()
    {
        // Call repos and setup sub navigation
        this.vm.SubNavigationList = SomeLogicSomewhere(this.currentPage);
        return this;
    }

    public TViewModel Build()
    {
        return this.vm;
    }
}

The controller and view

If you're in a controller where it's view don't need something you just skip that WithX() call.

[SessionState(SessionStateBehavior.Disabled)]
public class StandardPageController : PageController<StandardPage>
{
    public ActionResult Index(StandardPage currentPage)
    {
        StandardPageViewModel vm =
                    new LayoutViewModelBuilder<StandardPageViewModel,
                        StandardPage>(currentPage)
            .WithMeta()
            .WithBreadcrumbs()
            .WithSubNavigation()
            .Build();

        vm.PublishedSince = currentPage.StartPublish.ToTimeAgo();
        vm.CurrentPage = currentPage;

        return this.View(vm);
    }
}

The layout file of course uses the LayoutViewModel while the StandardPage view uses StandardPageViewModel.

@model LayoutViewModel
<!DOCTYPE HTML>
<html lang="sv" class="site no-js">
<head>
    <meta charset="utf-8" />
    <title>@Model.Title</title>

Published and tagged with these categories: Episerver, ASP.NET, Development, MVC