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