Get your hands dirty with ModelsBuilder... and your code cleaner

Last year I wrote about the differences between dynamic and strongly typed content access in Umbraco. My recommendation was to always use strongly typed content access. I also suggested to use Zbu.ModelsBuilder to generate strongly typed models for your doctypes.

A lot has happened since that article, and with the release of Umbraco 7.4 the models builder has been integrated into the Umbraco core and is now known as  Umbraco ModelsBuilder

Why use the models builder ?

Strongly typed content access has been available in Umbraco with the IPublishedContent interface, but the syntax is very verbose and prone to errors. To retrieve a property you would do something like this

@Model.GetPropertyValue<string>(“newsTitle”)

As you can see a typo in the property alias would make your code fail and you would never notice this until runtime. Also changing the property alias (not that I would recommend that) would require you to update your code everywhere the alias is used. To avoid these problems creating constants for your property alias would help out a lot, but then you are writing a lot of constants if you have a lot of documenttypes.

Using the models builder your syntax will be less verbose. Retreiving the same property now will look like this

@Model.NewsTitle

So this means it’s less prone to typos, but also when you change your document type the model will be regenerated and will show you compile errors instead of runtime errors.

How does it work

So let’s assume for simplicity that we have a NewsItem documenttype with one property called newsTitle.

The models builder will generate a class for that looking like this

public partial class NewsItem : PublishedContentModel 
{
	public string NewsTitle 
	{
		this.GetPropertyValue<string>(“newsTitle”);
	}
}

Now every time you request a content item of this document type it will return you the strongly typed object of type NewsItem because Umbraco’s content cache returns these natively.

So in your view you can put this on top and your model will be of type NewsItem

@inherits UmbracoViewPage<NewsItem>

The models builder respects the content type's inheritance, and content type's compositions are represented by interfaces.

To see how the models are generated then you can go into ~/App_Data/Models/models.generated.cs and /App_Data/Models/all.generated.cs

The models.generated.cs contains the code representing the documenttype in Umbraco. The all.generated.cs file contains your extensions to the models. We will cover extending models later in this article.

In case something goes wrong with the generation this folder will also contain a models.err file where the errors are logged. 

Using the models builder

As I mentioned, since Umbraco 7.4 the models builder is part of the core and will be enabled by default in PureLive mode.

To change in which mode the models are generated, you need to update the following appsetting in the web.config:

<add key="Umbraco.ModelsBuilder.ModelsMode" value="PureLive" />

To turn the models builder off entirely you can change the following appSetting in the web.config to false:

<add key="Umbraco.ModelsBuilder.Enable" value="true" />

More info on the possible values and more advanced web.config settings for the models builder can be found here: https://github.com/zpqrtbnk/Zbu.ModelsBuilder/wiki/Install-And-Configure

To see how the models builder is configured doesn’t require you to go in to the web.config. In the developer section of Umbraco there is now a “Models builder” dashboard. On this dashboard you can see how the models builder is configured. Depending on the mode it will have different options.

Dashboard.png
Models Builder dashboard in PureLive mode

ModelsBuilder comes with different modes for generating models. I will try to explain how they work and the differences between them.

Pure Live Models

This is the default mode with a new install and corresponds to the ModelsMode setting PureLive.

With PureLive models, the models are generated on the fly in memory, at runtime. So every time you make a change in a document type the models will be generated.

So in your view you can do this to render our previous news title property.

@inherits UmbracoViewPage<ContentModels.NewsItem>
@using ContentModels = Umbraco.Web.PublishedContentModels;
@{
	Layout = null;
}

@Model.NewsTitle

Or for getting an overview of child items of a certain type.

@inherits UmbracoViewPage<ContentModels.NewsOverview>
@using ContentModels = Umbraco.Web.PublishedContentModels;
@{
	Layout = null;
}

@foreach (var item in Model.Children<NewsItem>())
{
    <p>@item.NewsTitle</p>
}

As you can see, no "Where" clause is needed anymore to filter on doctype.

Downside to this mode is that the models are only avaible in views and no intellisense is available. So if you want to use your models in custom code this is probably not what you are looking for.

Dll Models

With Dll Models, models are generated and then compiled in to a dll that is copied to the bin folder. With the default config this will be Umbraco.Web.PublishedContentModels.dll.

To activate dll models you need to set the ModelsMode in the web.config to Dll or LiveDll.

The difference between the 2 modes is when the models are generated.

  • Dll : with this mode you need to go to the Models Builder dashboard in the developer section and push the “Generate Models”
  • LiveDll : with this mode the dll will be generated when you make changes to doctypes.
dashboard-dll.png
ModelsBuilder dashboard in AppData mode

For both modes generating the models will cause the Umbraco application to restart.

But now we can get intellisense when using the models in the views and the models are available in custom code like controllers, etc. You can also reference the generated dll in other Visual Studio projects.

AppData Models

With AppData Models the ModelsBuilder will only generate the models for you and that’s it. It’s up to you to make them available in your application, e.g. including them in a Visual Studio solution and compile them with your solution.

This mode is triggered by setting the ModelsMode in the web.config to AppData or LiveAppData.

The difference between the 2 modes is when the models are generated.

  • AppData : with this mode you need to go to the Models Builder dashboard in the developer section and push the “Generate Models” to update your models. 
  • LiveAppData: with this mode the model files will be generated on the fly whenever you make changes to doctypes, but compared to LiveDll doesn’t require an application restart.

Remember that I told you to look in ~/App_Data/Models/models.generated.cs and /App_Data/Models/all.generated.cs to see how the models are generated. Well, with AppData models these files don’t exist.

With AppData Models you will get a *.generated.cs file per doctype and media type you have in Umbraco e.g. NewsItem.generated.cs.

API Models

With API models, the ModelsBuilder does not generate models into your website, but it uses an API to expose the models to external tools. The easiest way to get started is the following:

Change the ModelsMode in the web.config to Nothing or remove the setting.

Then in Visual Studio install the ModelsBuilder API nuget package into your web project :

nuget install api.png
Nuget install Models builder APi

The package installation should add this appSettting to your web.config to enable the API:

<add key="Umbraco.ModelsBuilder.EnableApi" value="true" />

If that is not the case you can add it yourself.

TIP : run your website in debug mode otherwise the API will not work.

Now that we have our website setup for API mode we need to install the Umbraco Models builder Visual Studio Extension.

In Visual Studio go to Tools > Extensions and updates and search for Umbraco ModelsBuilder Custom Tool and install it. You can also download the it from here : https://marketplace.visualstudio.com/items?itemName=ZpqrtBnk.UmbracoModelsBuilderCustomTool

After you have installed the tool we need to configure it for our project. In visual studio go to Tools > Options and locate the Umbraco section and go to ModelsBuilder Options. Fill in the all the fields.

visual studio models builder options.png
Model builder options in visual studio
 

After that add classes in your project somewhere. For this demo I created a Models folder and added a Builder.cs file in there.

Then go to the properties window of this file (Righ Click > Properties). In the custom tool field, fill in “UmbracoModelsBuilder”.

custom tool config.png
Custom tool configuration on Builder.cs


Normally models should be generated now. If that is not the case you can also right-click the Builder.cs file and then “Run Custom tool”. In case of errors, check the output window of Visual Studio for more information.

Now if you click the expand icon on the Builder file you would see all your generated models like this.

visual studio generated classes.png
Classes generated by Models builder

What mode should I use?

So now that we covered all the different modes, let’s have a look at what you should use.

  • If you do not care about intellisense and do not plan to write any custom C# code, then PureLive is the way to go.
  • But if you want intellisense or use the models in controllers for example, you should go with Dll Models, AppData Models or the API Models.
  • Dll models is a good solution if your editor doesn’t have any compiling options. So you can let the ModelsBuilder do it for you. But you need to think if you put your generated dll into source control. This can cause issues when working with multiple devs on the same project. If you exclude, make sure you always generate the models when pulling from source control.
  • When using Visual Studio then you should use AppData or API Models. If you only have one project in your solution AppData is good solution. If you seperate your code in multiple projects then API Models are the best solution.

Personally I prefer the API models, but you can choose whatever mode you like. And the good news is you can always switch the mode your are using at any time.

Controlling model generation

Sometimes you want to overrule how models are generated. For example do not generate certain properties or implement your own logic for how a property is generated.

Excluding properties from models

In our website we are using the reserved property type alias “umbracoNaviHide” to control if a page should appear in the navigation.

The ModelsBuilder would generate this code for the property:

[ImplementPropertyType("umbracoNaviHide")]
public bool UmbracoNaviHide
{
get { return this.GetPropertyValue<bool>("umbracoNaviHide"); }
}


Because the IsVisible() method of an IPublishedContent already checks for a property with this alias we do not need to generate it on our content model.

So to prevent this property from being generated on your models you need to do the following:


In ~/App_Data/Models create a class file with the same name as your model. If you are using API models this will be the folder where you have the Builder class.

In our case it looks like this to exclude the property from generation.

  

namespace TwentyFourDays.Web.Models
{
    using Umbraco.ModelsBuilder;

    [IgnorePropertyType("umbracoNaviHide")]
    public partial class NewsItem
    {
    }
}

We tell the builder to not generate this property in our model by using the IgnorePropertyType attribute. If you are using the API Models you can add this to your builder class and this rule is applied to all document types.

Implementing our own logic

In our news item example from earlier on the newsTitle field wasn’t required. So if an editor leaves it empty we want to fall back to our item Name. We do no want to do this check everywhere in our views so we will change how this property is generated.

We will update our class for the NewsItem again to this :

namespace TwentyFourDays.Web.Models
{
    using Umbraco.ModelsBuilder;
    using Umbraco.Web;

    [IgnorePropertyType("umbracoNaviHide")]
    public partial class NewsItem
    {
        [ImplementPropertyType("newsTitle")]
        public string NewsTitle
        {
            get
            {
                var title = this.GetPropertyValue<string>("newsTitle");

                if (string.IsNullOrEmpty(title))
                {
                    return this.Name;
                }
                return title;
            }
        }
    }
}

Now, when we generate the models again the models builder knows not to generate the property, but to use the property we just created.

More on controlling the generation can be found here : https://github.com/zpqrtbnk/Zbu.ModelsBuilder/wiki/Control-Generation

Extending your models with 3rd party data

Nowadays a lot of websites are not built using CMS data alone, but also present data from 3rd party data sources.

For this example we want to display product data from a database. We have created a ProductPage doctype and generated a model for that. In our content tree will have one page of this type that will be used to show all products from the database. The URL of the page in the CMS is <domain>/product. The unique URL for each product will be this URL with SKU appended e.g.: <domain>/product/<sku>.

First we extend our content model for the product page with a property that will store the product information from the database.

namespace TwentyFourDays.Web.Models
{
    public partial class ProductPage
    {
        public ProductInfo ProductDetails { get; set; }
    }
}

To handle the requests for these product URLs we will implement a ContentFinder that will check if we have a product with the SKU from the URL in the database and display the product page. Otherwise it will just render a 404.

A simplified version of the content finder would look like this :

public class ProductContentFinder : IContentFinder
{       

    public bool TryFindContent(PublishedContentRequest contentRequest)
    {
           
		if (contentRequest == null)
        {
			return false;
        }
		
		// logic to determine if it's a product url and to get content item is  omitted
		
		var product = ProductRepository.GetBySku(sku);
		
		if(product == null)
		{
			// no product found
			return false;
		}
		
		// contentItem is of type ProductPage
		contentItem.ProductDetails = product
		
		contentRequest.PublishedContent = content;
		
		return true;
    }

}

With this logic we have product details available as property of our content model during the entire request. So we don’t need to write any logic in controllers or views (ughh) to get this data. Pretty cool right ?

Wrapping it up.

If you haven’t tried using the ModelsBuilder yet this article should help you get started and hopefully give you some ideas to do cool stuff with it.

Some interesting links:
Models builder wiki 
Comparing Models builder and ditto by Lars-Erik Aabech
uHangout about  ZbuModelsBuilder 
Lars-Erik talking at UK Fest about Models builder 


Happy holidays. And if you have questions… reach out on twitter.

Dave Woestenborghs

Dave is on Twitter as