View Models in ASP.NET MVC

Being more serious, this is an interesting topic that the advisors and p&p team discussed a few times while they were building the Reference Implementation for the soon-to-be-complete Web Client Guidance, and it is something I've heard discussed time and time again out in the field.

I usually describe the "M" in both MVC and MVP as "the rest of the application", i.e. the business logic and business entities, and implicitly everything below in the stack. Some people don't like this analogy, saying the "M" refers only to the classes used to pass data between Controller and View. I agree it is arguably inaccurate, but I find it a useful way to think about a design based on these patterns... not least because of some of the variations in design I've seen that reflect those described below.

Where discussion gets interesting is when people start asking what the View can depend upon (and for the rest of this post think MVC, not MVP). To dodge a lot of detail this comes down to the following options;

1.Business Entities are passed to a View as the "Model". Therefore the View has detailed knowledge of part of the business layer.

2.Business Entities are never passed to a View – instead the relevant data is extracted and placed in a new entity reserved purely for use by the UI layer... or a "View Model" as people call it today (back when I coded as a day job we called these UI Entities so don't go thinking this is something new!)

These options become particularly interesting when you consider that in many modern systems the Business Entities are actually basically Data Transfer Objects generated or consumed by a tool such as the Entity Framework, nHibernate, etc, etc. This is useful because they may have validation behaviour built in. Business Logic is usually implemented as POCOs that manipulate, create, or return these DTOs.

This means that in option 1 the UI has access to all the validation goodness that exists on these entities. What's more, you don't need to create a new set of View Models that almost exactly match your data layer, and a set of "mappers" that convert Business Entities into a View Model.

However... note that I said "... almost exactly match...". You see, in the vast majority of systems there is something other than just what is stored in your database that needs passing to the view. This could be the current user's login name, or the current date and time. Or perhaps we must pass Boolean flags to indicate whether or not various regions of the screen should be displayed based on the user's roles.

To illustrate the differences, let's see 3 different examples as to how you might pass model data to a view. I've blatantly plagiarised some of the great comments that Francis, Julian, and the advisors made here so don't credit me with all the hard work J

Variant 1: No View Model

In Variant 1 we have no View Model at all, so our controller simply retrieves data from the business logic as a business entity, and passes it to the view...

publicclassModelController : Controller

{

publicActionResult Index(int someparameter)

{

BusinessEntity model = BusinessLogic.GetModel(someparameter);

return View(model);

}

}

The code for this is simple, concise, and requires very little work on my part. However, what if I want to add a property to indicate whether or not the "login" region of the view should be rendered? What if the view needs some other model data? This approach very tightly couples not only the business logic to the UI, but potentially the database to the UI (depending on your ORM tool, and how your business entities are created).

To summarise;

1.(con) This approach is unlikely to suit real MVC applications, as there is usually additional data that must be passed across.

2.(con) It is not flexible – it is more likely to lead to additional code churn after go-live other than in the simplest of cases.

3.(con) Developers that don't know these patterns well may consider adding UI-focused fields to your business entity, which compromises your business layer.

4.(pro) MVC can take advantage of the same validation mark-up attributes (or other scheme) that your business layer uses.

5.You can use it, but be prepared to take the hit for changes later, and make sure your developers know how to evolve it into Variants 2 or 3.

The bottom line is that I would use this if I knew it was very unlikely that the view (perhaps a very focused partial view in a very simple system) would ever need more than a single business entity... but other people, including the p&p guys, rightly have strong concerns about this approach.

Variant 2: Container View Model

In Variant 1 we saw how little code was needed for the simple case, but there are warnings about the inflexibility and risk to the integrity of the business layer. Variant 2 aims to maximise the benefits of Variant 1, whilst minimising the code you must hand-crank by using a simple View Model container to pass multiple business entities (and potentially other types) to the view...

publicclassModelController : Controller

{

publicActionResult Index(int someparameter)

{

BusinessEntity model =

BusinessLogic.GetModel(someparameter);

OtherBusinessEntity othermodel =

BusinessLogic.GetOtherModel(someparameter, model);

ViewModelContainer container = newViewModelContainer

{

ShowLogin = !User.Identity.IsAuthenticated,

LoggedInName = User.Identity.Name ?? "",

TheEntity = model,

AnotherEntity = othermodel

};

return View(container);

}

}

publicclassViewModelContainer

{

publicbool ShowLogin { get; set; }

publicstring LoggedInName { get; set; }

publicBusinessEntity TheEntity { get; set; }

publicOtherBusinessEntity AnotherEntity { get; set; }

}

Here we have the "ViewModelContainer" class, that is designed to carry our payload of a couple of business entities and some other primitives. Our Controller is responsible for fetching the business entities and then assembling this container, before passing it to the view.

So let's think about our pro's and con's;

1.(pro) Any data not contained in our business entities can easily be passed across.

2.(pro) Change is easy to handle when it consists of additive data.

3.(pro) Developers will most likely add UI fields to the View Model container, not to the business entity.

4.(pro) MVC can take advantage of the same validation mark-up attributes (or other scheme) that your business layer uses.

5.(con) But... your business entities must still match the precise requirements of your view. If you want to convert or translate internal system concepts into something easier to display, this is difficult using this model.

Overall, this is my personal favourite as it provides flexibility and future proofing for little extra effort over and above Variant 1. However, it is by no means perfect and you must be willing to consider replacing your business entities with custom View Model entities if the needs of UI and business layer diverge... i.e. convert to Variant 3.

Variant 3: View Model and Mappers

Variant 3 is what could be called the utopia practice, in that it ensures your business and UI layers are suitably separated, with "mapper" classes (often referred to as the "Entity Translation" pattern) marshalling the relationship between the two. However, it comes at a price – and that is a little coding effort.

Let's see how this looks;

publicclassModelController : Controller

{

publicActionResult Index(int someparameter)

{

BusinessEntity entity = BusinessLogic.GetModel(someparameter);

MyEntityViewModel model =

MyEntityViewModelMapper.ConvertFromBusinessEntity(entity);

return View(model);

}

}

publicclassMyEntityViewModel

{

publicbool ShowLogin { get; set; }

publicstring LoggedInName { get; set; }

publicstring Name { get; set; }

publicbool IsOver18 { get; set; }

}

publicclassBusinessEntity

{

publicstring Name { get; set; }

publicint Age{ get; set; }

}

This time I've showed the BusinessEntity class too so that you can see how it differs from the View Model. We can see that the Controller fetches this entity and then uses a mapper class to convert it to the View Model class. This mapper might also access environmental variables (e.g. User.Identity) or I might pass in other data.

The point with this approach is that the business entities are never passed to the view - only View Model classes can be consumed by views. This leads us to some findings;

1.(pro) Any data not contained in our business entities can easily be passed across.

2.(pro) Change is easy to handle when it consists of data that should be added, removed (or not displayed), or even altered / mapped (such as our Age field).

3.(pro) Developers will add UI fields to the View Model, not to the business entity.

4.(pro) The UI's needs are kept distinct from that of the business logic.

5.(con) But... MVC cannot take advantage of the same validation mark-up attributes (or other scheme) that your business layer uses. Instead you must duplicate this.

Conclusion

The great news is that the p&p Web Client Guidance Reference Implementation includes some great examples of Variant 3. Check out the SongDetailsViewModel, and the Mapper classes. It quickly becomes clear how much freedom this separation of concerns gives us, and how easy to follow the pattern becomes when you use it as a convention.

Hopefully the Variants above help you understand why this approach was chosen, and help you pick an approach yourself. Make sure you check out the Reference Implementation to see how this can work in practice.