View Model Design And Use In Razor Views

The recommended practice for transferring data to and from views in ASP.NET MVC is to use a View Model. But what is a View Model? How to you design one and how should they be used? This article attempts to provide answers to those questions.

In the olden days of web development, developers used RecordSet objects or more recently DataSets or DataTables as a means to transfer data from the database to a view template. The View template might have been a classic ASP file containing a mix of HTML and server-side code, or a Web Form consisting of databound server controls like a GridView or ListView. Regardless, the data is untyped and working with it usually consists of referring to items by index or by using "magic strings" to reference data container values that borrow from the schema of the database that the data originated from.

The MVC framework introduced the idea of strongly-typed views to ASP.NET, primarily to provide some Intellisense support to working with data in Views, compile-time type checking, remove the need to cast objects to other types in the View and to provide the ability to scaffold Views from the Model that the View derives from. Developers can still pass "untyped" data to Views using the ViewBag (or ViewData) mechanism. This is the quickest way to throw arbitrary values at a View. In fact, the default MVC 3 templated Home controller shows how to pass the string ""Welcome to ASP.NET MVC!" to the Index View using ViewBag.

Strongly-typed Views feature the @model directive at the top of a Razor ViewEngine file, which specifies the actual type that the View derives from:

@model ViewModelSample.Models.MyModel

This is added automatically when you use the View creation dialogue and select the option to make the View strongly-typed:

The Model class dropdown will become enabled, and will list all classes available to your project. In this particular example, I have added an ADO.NET Entity Data Model derived from the Northwind sample database to the MVC application being used for illustrative purposes. You can see the familiar auto-generated Product, Customer and Category classes in the dropdown list above.

The name of the class is derived from the name and location of the View file, and inherits from WebViewPage<TModel>, and it is this that provides the strong-typing and IntelliSense support etc. If no View Model type is set in a model directive, the type that is used instead is dynamic:

What goes into the View Model?

This is the question that seems to be asked most often. So far as the Add View dialogue is concerned any class in the correct location is a candidate for a strongly-typed View. The collection of classes that were generated by the Entity Framework from the Northwind database are usually known as Domain Entities. It is not unusual to find Views deriving directly from these entities in tutorials and samples. One of the main reasons for this is that it is a quick route to generating demo-code. And sometimes it might even be appropriate where the system being developed is one that largely provides a CRUD application over those entities. If you want to create or update a Category in Northwind, all you really need is a CategoryName and Description property.

Data Annotation attributes are used to manage model validation at property level, as well as display labels and some aspects of scaffolding views. If the model class code is generated automatically, such as with the entity Framework, the file defining the domain entities is regenerated whenever the database is changed. You can use "buddy" or partial classes to apply attributes to domain entity properties. Here's a buddy class for the Category class:

It is empty, but has its own attribute - the MetadataType attribute which associates the source of the metadata to be applied to the Category class. In this case, the attribute points to a type called CategoryMetadata whose definition is as follows:

Regardless how many times the edmx file is regenerated, the metadata attributes are safe from harm.

The real world, however, is not often as straightforward. Usually, Views are complex and include artefacts from more than one domain entity. And perhaps only a subset of any entity's properties. The solution is to create a class whose sole role is to act as a container for a specific View's data. Or a Model for the View, if you will, or a View Model. A common approach to producing a View Model is to compose it from some domain entities and perhaps a sprinkling of properties. A View for adding a new product to the Northwind database will need fields for all of the Product properties together with a way of specifying which Category the new Product object belongs to. Here's something that will do the job:

The Product object comes directly from the domain entities generated by the Entity Framework. It will benefit from any validation or other attributes that may have been applied to a buddy class. The advantage of this approach is that code is reused in a DRY way, and the Product property needs little to no work once validated to prepare it for persistence by the data access layer.

Mappers

There is a school of thought that domain entities are not the place for setting validation rules or scaffolding and labelling instructions, because these are are purely presentational concerns. Therefore the entity should not be exposed to the presentation layer, even as part of a composite View Model class. There are also security concerns related to mass-assignment vulnerabilities and over-posting attacks where malicious users can craft HTTP requests that include values for entity properties that are not included in the HTML form. The default model binding within MVC will cause those values to be updated or added along with legitimate fields. A built-from-the-ground up View Model solves both these concerns. Rather than include a domain entity (and all of its properties), you only include properties that are required for the specific View. Taking this approach, the CreateProductViewModel will look slightly different:

Only those properties that need to be are exposed through the highly customised Vew Model. The Product class has a Discontinued property - a bool. This highly customised View Model doesn't include that property so any attempt to set the Discontinued value by over posting using the revised View Model will not work - so long as the parameter to the Controller Action accepts the View Model and not a Product object. So this approach helps to ensure separation of concerns and offers some additional security, but it means that the values posted to the controller need to be mapped to an entity to be persisted. The data layer deals with Product objects, not View Models. For fairly simple objects, that should be too much trouble:

However, mapping View Models to entities is made a lot simpler using a tool - AutoMapper, which is described as a convention-based object mapper. In other words, it maps one object (the View Model) to another (the Entity) based on the convention of properties on each object being named the same. It's actually more flexible than that - you can tell it to map mismatched properties too. AutoMapper is available from Nuget. You can access it within Visual Studio by going to Tools > Library Package Manager > Package Manager Console, then typing

PM> Install-Package AutoMapper

Once it has installed successfully, you simple create a mapping between two objects, then sit back:

My preference is to generate View Models specific for particular Views. While this may involve extra coding - and some might say a duplication of properties across entities and View Models, AutoMapper helps to minimise the additional work involved.

You might also like...

10 Comments

30 May 2012 21:27 - Zeljko

Shouldn't the line:
Mapper.CreateMap<CreateProductViewModel, Product>();
be called outside of the controller, at startup of the application?

31 May 2012 05:28 - Mike

@Zeljko,

Yes, you are right - thanks. The code here is example only, but I will amend things to make that clearer.

31 May 2012 06:38 - Zeljko

And something else regarding Automapper... Though I use it like you to map the viewmodel to the model, the author of Automapper doesn't think it's appropriate. He says Automapper was designed to 'flatten' the model (model to viewmodel conversion) but not the way round.

10 June 2012 15:59 - Jules Bartow

Mike,

You have the most cogent intro to ViewModels anywhere. Thank you.

However, inserts are easy. Most introductions seem to avoid the more difficult updates like the plague.

I'm looking for the next step in your excellent explanation with a similar walkthrough of using Lambda selection of joined tables between complex types with updates where a ViewModel represents subsets of multiple domain entities including Table per Type (TPT) inheritance with 1:0..1 relationships and the corresponding attaches and entry state changes using DbContext in Razor MVC pages.

Can you hold our hands (virtually) and coach us through the rest of the basics? Please.

I want to make ancestor errors and foreign key constraint violations a thing of the past.

14 June 2012 21:56 - Mike

@Jules,

That's more of an Entity Framework topic than an ASP.NET one.

22 October 2012 14:30 - Zipper

Great article Mike.

Any chance you can show how to retrieve the data as well as update it? I have yet to find a good example of all three scenarios on a semi-complex ViewModel (like yours which is perfect by the way in terms).

Thanks,Zip

03 March 2013 17:05 - Andi

I would like some property inheritance property. Something like that in your ViewModel:

27 March 2013 07:14 - Donald

Hi, May I please ask what the disadvantages of using the property directly are. I was referred to this blog from another where they said you are subject to Model Injection. Please clarify on this and how it can be done. I have another example I saw where their ViewModel derives a ViewModelBase<T> where T is the domain entity. So instead of have standalone backing properties, they have a ModelObject which is actually your domain entity. What is the best way of using your view models?

03 April 2013 12:51 - Mike

@Donald,

It would be useful if you said which blog referred you here so I can try to understand what they are talking about.

02 March 2016 15:52 - Andy

Can you please answer Zipper's question? Or Zip. Not sure what his/her preference is. But I'd also like to see this demoed with three tables, product, product_types_xref, and product_types where one product can have many product_types. I'm struggling to get that final piece of the puzzle. I currently have my entity model classes, but I need to create some pretty complex ViewModels to accomplish what you've done here. Thank you!

Unfortunately, something went wrong and your message or comments have not been submitted
successfully. I'll try to fix whatever the problem is as soon as I can.

Thanks for your comments. They have been successfully sent to me. I will try to respond
if necessary as soon as I can.