Using Models Builder in a project

— 8 minute read

A while back I gave an overview of the different options that Models Builder provides you. I didn't go into a lot of detail, but I did cover why you might want to use one option over another.

Structure permalink

Fast forward a little, and I think I've reached an approach that I'm happy with and allows me to build things in a way I want without adding extra complication. I'm more familiar with an MVC type scenario and so here's the kind of solution I normally end up with for Umbraco projects.

  • Project.Web.Core - class library
  • Project.Web.UI - website project (reference to the above)

There will likely be some other projects in there as well, e.g. tests.

I would typically place my models within the Core project and then utilise these within the views of the UI project. To do that, there are some configuration options to instruct Models Builder as to how to create models.

<add key="Umbraco.ModelsBuilder.Enable" value="true" />
<add key="Umbraco.ModelsBuilder.ModelsMode" value="AppData" />
<add key="Umbraco.ModelsBuilder.ModelsNamespace" value="Project.Web.Core.Models.Content" />
<add key="Umbraco.ModelsBuilder.ModelsDirectory" value="~/../Project.Web.Core/Models/Content/" />
<add key="Umbraco.ModelsBuilder.AcceptUnsafeModelsDirectory" value="true" />

The above will place the models generated by Models Builder into my Project.Web.Core project, so that I can then use them how I wish. Include them in the project and do a build, and they will be included in the project dll.

The good thing about this is if you already have a project that was using Model.Content.GetPropertyValue("propertyAlias") it's not too difficult to change them over to strongly typed models. Models Builder itself is essentially doing all that for you. If you're starting fresh on a project, then there a few other options for your model/mapping needs. None of the below generate models, so are quite different in usage to Models Builder. They do offer more complex scenarios in terms of mapping and granular view models, and may be something you prefer.

Using the models permalink

I've now got a bunch of files that provide me with the generated models from Umbraco. These are content models and they all inherit from PublishedContentModel. They are partial classes. When it comes to using these models, it might be instantiated via a controller or it might just be there for me via Umbraco's routing.

In a view, this is what we can do:

@inherits UmbracoViewPage<MyModel>

Then, within the view there will be intellisense and I will be notified of compilation errors. A nice benefit of using strongly typed models. Since we made use of UmbracoViewPage we also get access to all the Umbraco helper methods.

Ok, that sounds great. But, there's something a little iffy with using a model that Umbraco generated for me and is closely tied with the content in Umbraco. What if I wanted to extend this or have my own properties? Well, there's a solution for that. The official docs suggest that we create another partial class and add what we need there, but I think I prefer this method. We can just inherit from the model that Models Builder gave us.

public class FridayBeersViewModel : FridayBeers
{
public FridayBeersViewModel(IPublishedContent content)
: base(content) { }

// custom properties
public bool ShowBanner => BannerImage != null;
}

We've still got all the properties from the generated model and we can also build up a custom view model, which more closely matches the output in our views. As a side note, if you're view model ends up with little relation to the generated model then you probably shouldn't be inheriting from it and can just roll your own.

In the controller, you would then have something like this.

public class FridayBeersController : BasePageController
{
public ActionResult FridayBeers()
{
var model = new FridayBeersViewModel(CurrentPage);
// apply any other updates on the model
return CurrentTemplate(model);
}
}

I'm hijacking the route so that I can amend the default behaviour of Umbraco. I'm also making use of the template based routing in Umbraco, where the template name matches my action. BasePageController just inherits from SurfaceController and IRenderMvcController, and has a method called CurrentTemplate that resolves the template and passes the model to the view. Here's the example in Umbraco core: view on github.

Compositions permalink

The above example would work well for a page or a block of content, but we might want to break things up a little more and make better use of sharing code. When we use compositions, what we get from a generated sense is an interface and then our content models implement those properties. They can also make use of many compositions. This is great, because it means we can then define the interface as the model within a view if we want to. For example, if we had a interface such as IMetaData we can create a related partial that handles the meta tags on our site.

@inherits UmbracoViewPage<IMetaData>

<meta name="description" content="@Model.MetaDescription" />
// etc.

Or we might have a couple of pages that have a banner, and a few without. We just let our doc types use the compositions and they know how to render the banner.

ModelTypeAlias permalink

I'm sure at one point during development of an Umbraco website you would have created a class with some constants in them, relating to the alias of the doc type. An easy way to vary your code depending on the doc type. Well, Models Builder provides this in a straightforward way.

public partial class ExamplePage : PublishedContentModel
{
public new const string ModelTypeAlias = "ExamplePage";
public new const PublishedItemType ModelItemType = PublishedItemType.Content;

public BasePage(IPublishedContent content)
: base(content)
{ }

// other properties
}

To use this, just do ExamplePage.ModelTypeAlias when you need it.

Summary permalink

I've outlined a few ways in which you can use Models Builder on a project above perhaps what you get out of the box, so that you can get the most out of it in a simple manner. As a pattern, this should allow for easy extensibility and a quick way of consuming the content from Umbraco.

Further reading