Real MVC App using Umbraco – revisited

So, it seems that my latest post has generated a lot of debate throughout the umbraco community. Thank you!

Me and my coworkers at Lund & Andresen IT (in Danish), has been using this method for a couple of months now, and we’ve found a couple of issues that I didn’t think of. But now I have taken in some input from the community as well as from my coworkers and now I am revisiting the method.

The main theme throughout the post still remains: No data access or umbraco logic in the views! The views must only display what they are being served. Nothing more, nothing less.

Where I was wrong

In the old post, I suggested that all doctypes must be mapped to a model using a mapper class. This is no longer true! The problem quickly showed it self when building a large site: With mappers, we essentially get waay to much data per view, and we generalize how data is extracted without taking in account for different circumstances. An other problem was that our controllers no longer have any work to do, other than calling a mapper and serving a view. So my MVC (Model View Controller) became a “MMVC” (Model Mapper View Controller), which was not intended.

Sample project

As promised on twitter, I have made a simple sample project that illustrates my points. Please feel free to download it and tell me what you think.

What has changed

Not that much has changed. I have only redefined the mapper roles and reinstated the controller roles:

Models

I have two types of models in mind for a basic umbraco site:

  • Document type model
  • Data model

Document type models represent real document types. They have the same inheritance structure as doctypes, meaning if a doctype is a child of a master doctype so must the representative model.
So if you have a doctype tree looking like this:

  • MasterDocType
    • TextPage

Then you will have these two models:

public class MasterDocTypeModel{}
// Please note, we inherit from the MasterDocTypeModel:
public class TextPageModel : MasterDocTypeModel {}

This way, when you add a property on the MasterDocType, you will only have to add said property on one model.

Besides containing properties mapped from a doctype, a Document Type Model, may also contain other properties, like menu items.

Data models are models that are not in any way related to doctypes. A great example is for menus. You don’t want to build an entire document type model for each item and descended items in a menu tree, when the only information you want are “Name”, “Url”, “IsActive” and “ChildNodes”.

This is where data models come in. A menu would list NavigationItemModels instead of a mixture of TextPageModel, NewsArchiveModel and so on and so forth.

Controllers

Controllers are more important than ever!

Controllers are the ones that builds models and serves them to the views. In other words, controllers build Document Type Models, and passes them to the views.

Again, I have two different types of controllers:

  • Master controllers
  • Document type controllers

Essentially, there must be a master controller for each master doctype, (a master doctype is a document type that has children), and a document type controller for each document that has a template attached.

So in my example above, we should have a master controller called MasterDocTypeController. This controller inherits from Umbraco.Web.Mvc.RenderMvcController and should not contain any actions!
The only thing these controllers must contain are two overloads of the inherited View()-method:

protected ViewResult View(MasterDocTypeModel model)
{
    return this.View(null, model);
}
protected ViewResult View(string view, MasterDocTypeModel model)
{
    // TODO: Set master doctype model values.
    return base.View(view,model);
}

By creating these two methods, we are able to set values that are to be set on all models that inherits from MasterDocTypeModel.

Please note, Master controllers, can also inherit from each other, depending on the doctype structure.

A doctype controller, would then inherit from this master controller, and set values essential for said doctype and return the result from the View-method created before.

Mappers

Mappers have a much lesser role, but not less important role! Mappers are used to map data models. Data models are shared across controllers and are not doctype dependent, so we will only have a few properties to map, and can to it in a very generalized way.

Data models are inheritable, and this inheritability must be addressed in the mappers as well. It’s quite simple, so why not just do it when we build the mappers?
A mapper class is a static class with a single static method called Map().

It looks like this:

internal static class NavigationItemMappers
{
    internal static TModel Map(TModel model, IPublishedContent content)
        where TModel : NavigationItemModel
    {
        // TODO: Add mapping logic here.
        return model;
    }
}

Please note that the method is generic. This way we can call the mapper like so: Map(new SomeInheritedModel(), CurrentPage) and get SomeInheritedModel back, on which we can continue our work without type casting. This is quite useful in linq statements:

IEnumerable original = from c in someSource
                                            select NavigationItemMappers.Map(new NavigationItemModel(),CurrentPage);

// This works, as well as the one above:
IEnumerable inherited = from c in someSource
                                            select NavigationItemMappers.Map(new SomeInheritedModel(){
                                            SomeProperty = c.SomeValue
                                            },CurrentPage);

// This also works:
IEnumerable inheritedToOriginal = from c in someSource
                                                       select NavigationItemMappers.Map(new SomeInheritedModel(){
                                                       SomeProperty = c.SomeValue
                                                       },CurrentPage);

// This does not:
IEnumerable originalToInherited = from c in someSource
                                                      select NavigationItemMappers.Map(new NavigationItemModel(),CurrentPage);

As you see, same mapper, different methods and different types, meaning greater flexibility.

A not on the mappers: Mappers should not map lists or child elements! These can differ from page to page. What works on a front page might not work as well on descending pages.

To summarize

The original post is still valid!
Controllers must have greater responsibility and must be the ones to handle doctypes, not mappers!
Mappers should map only simple data models and not map lists or child elements.
Controllers must use mappers as they see fit. Controllers control the mappers, not the other way around!

  • Danny

    This has been amazingly helpful … This is my first foray into the MVC architecture / ideology, AND I am brand new to Umbraco …

    You are the man.

  • Thank you! You’re welcome, glad I could help.

  • Danny

    Since we’re using a custom model in your examples, I’m a little unsure how to go about accessing the Umbraco model class for things members like GetPropertyValue or Media.GetImage?

    • Those methods will be called in the controller.

      All umbraco related stuff, must be called from either a controller or a mapper. Not in the views.

      Exactly like my examples.

      So when you build your models, you should set the properties like you’d do in your views in “ye olde times”.

  • I am getting an error when I try to open the sample project in VS 2012:
    “The imported project “D:\Study\CodeSamples\Umbraco\MVC\packages\Microsoft.Bcl.Build.1.0.8\tools\Microsoft.Bcl.Build.targets” was not found”

    Can you help?
    Thanks

    • Hi,

      You have to enable nuget package restore. And you propably have to build the solution a couple of times, before the targets file is recognized.

  • Love this article. Really great insight into MVC in umbraco. I’m admittedly relatively new to umbraco but have a solid background in .Net MVC framework. I’m curious as to how you would have several post actions within your controllers using this approach. For instance I have an account controller with several actions for displaying profile, registering and logging in. I need post methods on all of those. Any suggestions for how I should handle that?

    • Hi, thanks. It’s always great to hear people like my work.

      In umbraco, you are not able to call actions directly on a rendermvccontroller. What you need is a surfacecontroller. It’s the same principle as regular mvc.
      So your setup is:
      RenderMvcController for rendering page content and SurfaceController for forms.
      With surfacecontrollers, you are able to use the build in Html/Ajax.BeginForm(action,controller).

  • PM

    I am having the same problem as another user above – when I load the sample project “packages\Microsoft.Bcl.Build.1.0.8\tools\Microsoft.Bcl.Build.targets” is missing??? What a shame you didn’t upload a project that works straight away. I have enabled nuget package restore but it makes no difference. The solution cannot be built because the project file won’t load.

  • Hi, I’m sorry to hear that. I will look into it ASAP.
    The problem has been fixed, it’s just a long time ago since I created the project and didn’t experience the problem.

    It may take a couple of hours until I have fixed it.

  • PM

    For people trying to get the sample solution working, delete the following two lines from the csproj file:

    Then build, and nuget package restore is able to download the references.

  • Problem has been solved. The project should run as expected now.

  • What about default Umbraco page propertis like Name, ID and so on. How do you make them accessible in the view?

    • Hi,

      Those properties should, like anything else, be passed to the view in the view model.

  • Efe

    Nick,
    I appreciate the effort you taken to develop and share this. I have a question about implementing a Surface Controller.
    I’ve created a search controller inherited from a Surface Controller. The model to return is derived from my mastermodel class (not inheriting from RenderModel).
    The view rendering this model gives the error.
    “The model item passed into the dictionary is of type ‘Umbraco.Web.Models.RenderModel’, but this dictionary requires a model item of type ‘XXX’.”

    In my layout.cshtml, the model is @model dynamic. Any thoughts?

    • Hi, thanks for the feedback.

      have you tried setting @model to a different type than dynamic? like setting it to the one that your controller returns.

  • Efe

    Initially in layout.cshtml I had the model as a model derived from the mastermodel. The model that is to be rendered in the partial view also derives from the same model.

  • Efe

    I forgot to ask have you worked with Surface controllers using this architecture?

    • well I haven’t used surface controllers like this, I primary uses then for json results for Ajax calls.

      maybe umbraco does something with the surface controller view results that I’m not aware of.
      I will look into it.

  • This looks like a great way to set up the web site itself for content managed by the Umbraco interface. Now, assume you need to create an application within the Umbraco site for users to register and pay for an event. You create a new VS project/solution. How do I structure this to integrate seamlessly within the Umbraco site? In the Umbraco 4.x days, I’d make a userControl and drop a macro into my textpage text control.

  • Drew

    Hey great article. I’m just getting into Umbraco too and I appreciate the push in the right direction.

    I wanted to ask about using Umbraco.Fields (from the dictionary) with your approach above. By changing the view from inheriting from UmbracoViewPage or UmbracoTemplatePage to a specific model, you lose the ability to get dictionary items. Thought? I have a project that will be localized for multiple languages, so having the dictionary available would be a great help. Thanks in advance, Drew

  • @Connie,

    Sorry for the late response, had problems with my email provider, so I was not notified of your comment.

    When you structure your umbraco project. These folders are essential:

    Controllers
    Models
    Views
    —Partials
    —Shared

    Controllers and models are self-explanatory.
    The Views folder contains all your templates. In ye olde v4-days, these would’ve been masterpages. Today they are razors. Furthermore, this folder needs an “_ViewStart.cshtml”-file, with a reference to a “_Layout”-view in the “Shared”-folder.

    All your macro’s and reusable (partial) views, should be put inside the “Partials”-folder.

    I have not been using macros my self with this approach, so this is up for experiment. Please let me know of your results.

    @Drew,

    When accessing the dictionary items, you have multiple options:

    1: Receive the values in the controller, and add them to your ViewBag or Model.
    2: Create a helper, that makes it easy to fetch dictionary values without having an “UmbracoContext” at hand and call that helper from within the views.
    3: Use APE, and call Dictionary.SomeKey.

    I’m no big fan of adding dictionary items to the model, and the viewbag is no good, since its dynamic and what not and creating a helper might take a lot of effort for very little reward.

    I would highly recommend using my package “APE”, http://ndesoft.dk/2014/06/10/create-magic-with-ape/.
    All it does is creating strongly typed access to doctypes and their properties, and, in this case, static access to Dictionary- keys and values.

  • Thanks for a great article that helped me to better understand how the MVC thoughts is best applied to Umbraco. I myself believe that the extra work it will be with the MVC and not to mix code and presentation pays off in the long run. By the way what is best practice to add a form in Umbraco using your approach.