Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Modelsbuilder introduction
Models Builder is a tool that can generate a complete set of strongly-typed published content models for Umbraco. By default, a slimmed down version of Models Builder is embedded with the main Umbraco distribution.
Models can be used anywhere that content is retrieved from the content cache, i.e. in MVC views, controllers, etc. In other words, when using the Models Builder, the content cache does not return IPublishedContent
objects anymore, but strongly typed models, implementing IPublishedContent
.
For each content, media and member type in the Umbraco setup, the generator creates a *.generated.cs
file, corresponding to the type. For instance, a document type with a textstring property named Title, and a rich text editor named BodyText will look like this:
Now since this is an automatically generated file, it's a bit messy, the important part is that it has all the properties defined and strongly typed:
And:
Umbraco's content cache returns these objects natively: No need to map, convert or anything; the following code runs:
If your view inherits from UmbracoViewPage<NewsItem>
then the model is the content item itself and the syntax is @Model.Title
.
Models Builder respects the content types' inheritance tree, i.e. models inherit from each other if required, and mixins (content type compositions) are represented by interfaces.
Models Builder is a "code-after*_" solution. It only generates code from content types that already exist in Umbraco. It is not a "code-first" solution - code-first is a much more complex question.
And once you are using strongly typed models, there are some cool things that you can do.
The Models Builder is by default embedded in Umbraco. If you need more complex features than what is provided, you still need to add the full package. However, as of right now the package is not updated to be able to handle NetCore or Umbraco V9.
Check the official releases on the Models Builder GitHub repository for more details.
At the core of the strongly typed models "experience" is the IPublishedModelFactory
interface. This interface is part of the Umbraco core codebase. It is responsible for mapping the internal IPublishedContent
implementations returned by the content cache, to strongly typed models. There is a default factory shipped with Umbraco and it is possible to replace this by custom implementations. When using the default factory, models do not necessarily need to be generated by Models Builder.
Models Builder is one way to generate models for the default, built-in factory. Models can be generated automatically or straight from the Settings section of the Umbraco backoffice, for more info see builder modes.
Modelsbuilder reference
The Models builder is a tool that can generate a complete set of strongly-typed published content models for Umbraco. Models are available in both controllers and views.
Modelsbuilder modes
Models Builder can be used in different modes:
InMemory models
SourceCode models
The mode is indicated by the Umbraco:CMS:ModelsBuilder:ModelsMode
key in the configuration (appsettings.json
files).
Corresponds to the InMemoryAuto
setting value.
With InMemory models, models are generated and compiled on the fly, in memory, at runtime. They are available in views exclusively.
This is for a setup that exclusively uses the Umbraco backoffice, and do not use custom code such as controllers. Whenever a content type is modified, models are updated without restarting Umbraco (in the same way .cshtml views are recompiled).
Generation can fail for various reasons, in which case Umbraco will run without models (and front-end views fail to render). Umbraco's log file should contain all details about what prevented the generation, but it is probably faster to check the Models Builder dashboard, which should report the last error that was encountered, if any.
Models Builder maintains some files in ~/umbraco/Data/TEMP/InMemoryAuto
:
models.generated.cs
contains the generated models code
all.generated.cs
contains the compiled code (models merged with non-generated files)
models.hash
contains a hash code of the content types
all.dll.path
contains the path to the compiled DLL file containing all the models
Compiled/generated.cs{GUID}.dll
the dll containing all the generated models
models.err
contains the last generation error information, if any
The models.hash
file is used when Umbraco restarts, to figure out whether models have changed and need to be re-generated. Otherwise, the local models.generated.cs
file is reused.
Corresponds to the SourceCodeManual
and SourceCodeAuto
setting values.
With SourceCode models, models are generated in the ~/umbraco/models
directory, and that is all. It is then up to you to decide how to compile the models (e.g. by including them in a Visual Studio solution).
Generation can fail for various reasons, in which case no models are generated. Umbraco's log file should contain all details about what prevented the generation, but it is probably faster to check the Models Builder dashboard, which should report the last error that was encountered, if any.
The modelsbuilder works much in the same way whether using SourceCodeManual
or SourceCodeAuto
. The only real difference between the two are that with SourceCodeManual
you must manually trigger the generation of the models from the models builder dashboard, whereas with SourceCodeAuto
the models are automatically generated whenever content types change.
These modes are not available in the embedded version of Models Builder. See the full version of ModelsBuilder.
Understand and extend modelsbuilder
Models are generated as partial classes. In its most basic form, a model for content type TextPage
ends up in a TextPage.generated.cs
file and looks like:
What is important is the Header
property. The rest is (a) a constructor and (b) some static helpers to get the PublishedContentType
and the PublishedPropertyType
objects:
Content type Compositions in Umbraco allows content types to inherit properties from multiple other content types. Unlike C#, where a class can only inherit from one other class, Umbraco content types can be composed of multiple other content types.
In Umbraco v14, the traditional .NET Inheritance feature has been removed. Instead, properties are inherited through Composition, allowing for greater flexibility in managing content types.
For example, TextPage
content type could be composed of MetaInfo
(inheriting properties such as Author
and Keywords
) and PageInfo
(inheriting Title
and MainImage
) content types.
Each content type involved in a composition is generated both as a class and as an interface. Thus, the MetaInfo
content type would be generated as follows (some code has been removed and altered for simplicity's sake):
And the TextPage
model would be generated as follows (some code has been removed and altered for simplicity's sake):
In the Umbraco Backoffice, a content type can appear underneath its parent content type in the content tree.
By convention, a content type inherits properties from its parent in a compositional manner. However, this does not use traditional class inheritance but rather a compositional approach. This approach ensures content types can combine multiple sets of properties without the limitations of single inheritance.
Therefore, assuming that the AboutPage
content type is a direct child of TextPage
, it would be generated as:
Because a model is generated as a partial class, it is possible to extend it. That could be by adding a property, by dropping the following code in a TextPage.cs
file:
Models builder does not take a custom partial class into account when generating the models. This means that if a custom partial class, inherits from a base class, tries to provide a constructor with the same signature, or implements a generated property, it will cause compilation errors.
Furthermore a generated model will always be instantiated with its default constructor, so if an overloading constructor is created it will never be used.
For more complex partial classes, you'll have to use the full version of the Models Builder.
As of Umbraco 11.4, the IModelsGenerator interface has been added. If you want to customize how the models are generated, you can make your own implementation of the IModelsGenerator
interface. You can then overwrite the Umbraco implementation with dependency injection.
The interface can be accessed via Infrastructure.ModelsBuilder.Building.ModelsGenerator
.
Extending models should be used to add stateless, local features to models. It should not be used to transform content models into view models or manage trees of content.
A customer has "posts" that has two "release date" properties. One is a true date picker property and is used to specify an actual date and to order the posts. The other is a string that is used to specify dates such as "Summer 2015" or "Q1 2016". Alongside the title of the post, the customer wants to display the text date, if present, else the actual date. If none of those are present, the Umbraco update date should be used. Keep in mind that each view can contain code to deal with the situation, but it is much more efficient to extend the Post
model:
And to simplify the view as:
Because, by default, the content object is passed to views, one can be tempted to add view-related properties to the model. Some properties that do not belong to a content model would be:
A HomePage
property that would walk up the tree and return the "home page" content item
A Menu
property that would list the content items to display in a top menu
Etc.
Generally speaking, anything that is tied to the current request, or that depends on more than the modeled content, is a bad idea. There are much cleaner solutions, such as using true view model classes that would be populated by a true controller and look like:
One can also extend Umbraco's views to provide a special view helper that would give access to important elements of the website, so that views could contain code such as:
The scope and life-cycle of a model is not specified. In other words, you don't know whether the model exists only for you and for the context of the current request, or if it is cached by Umbraco and shared by all requests.
As a consequence, the following code has a major issue: the TextPage
model "caches" an instance of the HomePageDocument
model that will never be updated if the home page is re-published.
As a rule of thumb, models should never, ever reference and cache other models.
Explanation of how to configure models builder
The following configuration option can be set in the application settings (in the appsettings.json
file):
Umbraco.CMS.ModelsBuilder.ModelsMode
determines how Models Builder generates models. Valid values are:
Nothing
: Do not generate models.
InMemoryAuto
(default): Generate models in a dynamic in-memory assembly.
SourceCodeManual
: Generate models in ~/umbraco/models
(but do not compile them) whenever the user clicks the "Generate models" button on the Models Builder dashboard in the Settings section.
SourceCodeAuto
: Generate models in ~/umbraco/models
(but do not compile them) anytime a content type changes.
Umbraco.CMS.ModelsBuilder.ModelsNamespace
(string, default is Umbraco.Cms.Web.Common.PublishedModels
) specifies the generated models' namespace.
Umbraco.CMS.ModelsBuilder.FlagOutOfDateModels
(bool, default is true
) indicates whether out-of-date models (for example after a content type or Data Type has been modified) should be flagged.
Umbraco.CMS.ModelsBuilder.ModelsDirectory
(string, default is ~/umbraco/models
) indicates where to generate models and manage all files. Has to be a virtual directory (starting with ~/
) below the website root (see also: AcceptUnsafeModelsDirectory
below).
Umbraco.CMS.ModelsBuilder.AcceptUnsafeModelsDirectory
(bool, default is false
) indicates that the directory indicated in ModelsDirectory
is allowed to be outside the website root (e.g. ~/../../some/place
). Due to this being a potential security risk, it is not allowed by default.
Umbraco.CMS.ModelsBuilder.DebugLevel
(int, default is zero) indicates the debug level. Set to greater than zero to enable detailed logging. For internal / development use.
The example below shows an example configuration using the SourceCodeManual mode.
It is recommended to generate models in your development environment only and change the ModelsMode to Nothing
for your staging and production environments.
Models Builder ships with a dashboard in the Settings section of Umbraco's backoffice. The dashboard does three things:
Details on how Models Builder is configured
Provides a way to generate models (in SourceCodeManual mode only)
Reports the last error (if any) that would have prevented models from being properly generated
Using interfaces with modelsbuilder
When using compositions, Models Builder generates an interface for the composed model, which enables us to not have to switch back to using Value()
for the composed properties.
A common use-case for this is if you have a separate composition for the "SEO properties" Page Title
and Page Description
.
You would usually use this composition on both your Home
and Textpage
document types. Since both Home
and Textpage
will implement the generated ISeoProperties
interface, you will still be able to use the simpler models builder syntax (e.g. Model.PageTitle
).
However, you won't be able to use the nice models builder syntax on any master template, since a master template needs to be bound to a generic IPublishedContent
. So you'd have to resort to the ever-so-slightly clumsier Model.Value("pageTitle")
syntax to render these properties. It is possible to solve this issue of master templating, by using partial views, to render the SEO specific properties.
If you create a partial and change the first line to use the interface name for the model binding, you can use the nice Models Builder syntax when rendering the properties, like this:
You can then render the partial from your Master Template with something like this (assuming the partial is named Metatags.cshtml
):
It's important to note though, that this master template will only work for content types that use the Seo Properties composition.
Cool things you can do with models
It's possible with Razor to define functions for rendering HTML. We can leverage our strongly typed models when doing this, and even provide overloads for different types of models. That will automatically be called for different models using dynamic
It's not recommended to create a template and doing all the rendering via razor function, but it can be nifty for rendering search results.
A thing that's important to note here is that RenderContent
is called from a codeblock, and not as @RenderContent((dynamic) Model);
the reason for this is that if you try to use the latter, razor will expect for the function to return something for it to render.
By casting the strongly typed to a dynamic when calling the RenderContent method, you tell C# to do late runtime binding. You also tell it to pick the proper RenderContent implementation depending on the actual Common Language Runtime (CLR) type of the content object. Using dynamic here is OK and will not pollute the rest of the code.