14 Days Free Technical Video Training from WintellectNOW

  

Use ViewModels to manage data & organize code in ASP.NET MVC applications

Tags: ASP.NET, MVC 3, ASP.NET MVC, ViewModels

The concept of the ViewModel isn't just for ASP.NET MVC, as you'll see references to ViewModels throughout the web in articles and blog posts about the MVC, MVP, and MVVM patterns. Those posts and articles can center around any number of technologies such as ASP.NET, Silverlight, WPF, or MVC... This post will investigate ViewModels as they apply to the world of ASP.NET MVC.

What is an ASP.NET MVC ViewModel?

In ASP.NET MVC, ViewModels allow you to shape multiple entities from one or more data models or sources into a single object, optimized for consumption and rendering by the view. The below image illustrates the concept of a ViewModel:

image

The purpose of a ViewModel is for the view to have a single object to render, alleviating the need for UI logic code in the view that would otherwise be necessary. This means the only responsibility, or concern, of the view is to render that single ViewModel object, aiding in a cleaner separation of concerns (SoC). Concerns are distinct aspects of the application that have a particular purpose (i.e., concern), and keeping these aspects apart means your application is more organized, and the code more focused. Putting data manipulation code in its own location away from the view and controller, enforces SoC.

Using ViewModels in MVC for finer granularity and better SoC leads to more easily maintainable and testable code. Remember, unit testing is about testing small units.

Along with better coding practices, there are many business reasons demonstrating why you might consider using ViewModels:

  • Incorporating dropdown lists of lookup data into a related entity
  • Master-detail records view
  • Pagination: combining actual data and paging information
  • Components like a shopping cart or user profile widget
  • Dashboards, with multiple sources of disparate data
  • Reports, often with aggregate data

The above scenarios are common to a wide variety of applications, and deal with more complex data than basic CRUD forms-over-data page (e.g., a simple 1:1 mapping to the db table). For example, providing a list of states, and ensuring that the state that matches the state of current customer, means that you need to either provide two sets of data or a single set of customer/state data combined, as shown in the image below.

image

Some scenarios such as a lookup table representing states in the USA, could easily work with either ViewModels or a ViewBag/ViewData object, so there is some potential overlap at times. It's up to the application architects and developers to decide what works best with their exact use case.

Creating a ViewModel

Although a ViewModel consists of multiple entities, at its core a ViewModel is still just a class - and one that doesn't even inherit from anything special, as many MVC classes do. 

Physically, ViewModels can exist in different locations, listed below:

  • In a folder called ViewModels that resides in the root of the project. (small apps)
  • As a .dll referenced from the MVC project (any size app)
  • In a separate project(s) as a service layer, for large applications that generate view/content specific data. (enterprise apps)

Since a ViewModel is just a class, the easiest way to get started using one is to create a new folder named ViewModels and add a new code file to it.

To create the CustomerViewModel ViewModel, add the Customer and StatesDictionary types as properties to form one CustomerViewModel class. In the example below, the CustomerViewModel class contains the newly defined properties.

public class CustomerViewModel 
{
    public Customer Customer { get; set; }
    public StatesDictionary States { get; set; }
    public CustomerViewModel(Customer customer)
    {
        Customer = customer;
        States = new StatesDictionary();
    }
}

Generally, ViewModels contain the word "ViewModel" as part of its name; however, the conventions at work here are for consistency in code readability, since other classes in MVC state their intents in their names as well (e.g., names of controllers, action methods, etc...use conventions in their names).

The StatesDictionary class is a simple Dictionary object containing two type parameters of type string. The class also contains the definitions for all the members in the Dictionary (i.e., the state data). The only property in the StatesDictionary class is the StateSelectList, which is an object that Html Helpers use with to render an HTML <select> element that displays a listing of states. The type Dictionary<string, string> in the StateSelectList property maps to the state abbreviation then state name, respectively.

public class StatesDictionary
{
    public static SelectList StateSelectList
    {
        get { return new SelectList(StateDictionary, "Value", "Key"); }
    } 
    public static readonly IDictionary<string, string> 
        StateDictionary = new Dictionary<string, string> { 
      {"Choose...",""}
    , { "Alabama", "AL" }
    , { "Alaska", "AK" }
    , { "Arizona", "AZ" }
    , { "Arkansas", "AR" }
    , { "California", "CA" }
    // code continues to add states...
    }; 
}

Data that lives in small lists and infrequently changes, like the StatesDictionary class, exists in all types of applications. In real world applications, you'll find a variety of methods for dealing with lookup data such as a list of states - often XML files and SQL tables. You can replace the code in the StateDictionary method to use entities from Entity Framework, read data from files, or any data access code that you require.

After creating the ViewModel, the next steps are to instantiate it in a controller and return it to the view.

Getting the ViewModel to the view

Starts with the controller...

Sending a ViewModel to the view for rendering will work the same as when dealing with a model. Since it's just a class, the view doesn't know, and doesn't care, where the model or ViewModel came from. You can create the instance of the ViewModel class in the controller, or resolve it if using an IoC container. Remember that just as you would do with views, you should keep controllers clean of unnecessary code, meaning that only code that fetches the model or ViewModel belongs here, and little more.

public ActionResult Edit(int id)
{
    Customer customer = context.Customers.Single(x => x.Id == id);
    var customerViewModel = new CustomerViewModel(customer);
    return View(customerViewModel);
}

Then the view renders the ViewModel...

In order for the view to know what object to use, set the @model keyword to point to the ViewModel, just like you already would with a regular model.

@model FourthCoffee.Web.ViewModels.CustomerViewModel

Because the Customer object is a property of the ViewModel, you'll see the model.Class.Property syntax to access the ViewModel data, similar to the following line of code.

<div class="editor-label">
    @Html.LabelFor(model => model.Customer.FirstName)
</div>
<div class="editor-field">
    @Html.EditorFor(model => model.Customer.FirstName)
    @Html.ValidationMessageFor(model => model.Customer.FirstName)
</div>
@* ...View code continues rendering properties... *@

Additionally, you can edit the Edit/Create views so that the DropDownList containing a list of the states will display, and display the correct state matching that of the customer.

<div class="editor-field">    
    @Html.DropDownList("state", new SelectList(StatesDictionary.StateSelectList, 
                       "Value", "Text", Model.Customer == null ? "" : Model.Customer.State))
    @Html.ValidationMessageFor(model => model.Customer.State)
</div>
As you might have noticed, using a ViewModel is just as easy as using the ViewBag or ViewData objects. ViewModels, however, provide those extra benefits like being easier to test and optimize.

Checking the results

After a user navigates to the /Customers/Edit/1 URL in the browser, the Razor view engine renders the CustomerViewModel similarly to the following screen shot.

image

The State DropDownList displays the states and the current state for that customer, as expected.

Digging Further into ViewModels

Because ViewModels render pre-manipulated data that no longer have those 1:1 mappings between model classes and database tables, you'll need to do create mappings yourself. You can manually map small ViewModels, but this will quickly become burdensome when mapping larger classes, especially when working with parent-child-grandchild, multi-level, or complex data. This is where a tool such as AutoMapper comes into play. AutoMapper will let you fluently setup mappings between ViewModels and models more easily than doing so manually, or writing your own mapper.

Here are some tips for using ViewModels:

  • Put only data that you'll render in the ViewModel.
  • The view should direct the properties of the ViewModel, this way it fits better for rendering and maintenance.
  • Use a mapper when ViewModels become complex.

Some tools that can help assist you in generating POCOs (Plain Old CLR Objects) for models and ViewModels are:

POCO Generator

EF POCO Templates

In addition to these tools, you can use MvcScaffolding to create actions and views based on ViewModels. MvcScaffolding, invention of ASP.NET team member Steve Sanderson, gives you more power in creating CRUD, repository, unit test and other templates quickly and painlessly. Check out Steve's Multi-part series on MvcScaffolding here. MvcScaffolding works with ViewModels as well as models.

You should always prefer using a ViewModel rather than instantiating multiple models and putting that manipulation code in the controller.

Summary

ViewModels help you organize and manage data in MVC applications when you need to work with more complex data than the other objects allow. Using ViewModels gives you the flexibility to use data as you see fit. ViewModels area generally a more flexible way to access multiple data sources than models + ViewBag/ViewData objects.

Further reading: Comparing the MVC and MVVM patterns along with their respective ViewModels

Download the code!

32 Comments

  • Kamran said

    Yep, I hardly ever code views against actual domain models; it's almost always a View Model. In our enterprise app, we serve the view models directly from our app services. We use Automapper to map many of our models, although we discovered that Automapper can be insanely slow with complex supertype-subtype relationships in lists (so we do that manually using extension methods). The end result brought the mapping time down from 2 seconds to only 50ms. So on that note, be careful when using AutoMapper or other reflection-based mapping methods!

  • Bart Czernicki said

    One thing u should add for a best practice is that while ViewModels don't have to "inherit" from a base class it is a good practice to create interfaces. This way you can "inject" whatever ViewModel you want, like u say only the data u "need" for example a Tablet vs Mobile vs full Web ViewModel -> View

  • Dan said

    @Bart: Yes, I thought the same thing, implement a common interface for the viewmodels. Helps with testing as well.

  • blachniet said

    Thanks for this post. I'm new to MVC, with a very small amount of experience in MVVM in WPF, and was wondering if view models were generally acceptable in MVC designs. Now I can move forward with a clear conscience.

  • blachniet said

    After scanning over this post again, the following line caught my attention:
    "you should keep controllers clean of unnecessary code, meaning that only code that fetches the model or ViewModel belongs here"

    As I mentioned in the previous comment, I am brand new to MVC. Where does business logic usually go in MVC? For example, if I want create a new Foo database entry every time a new user registers, where does logic for this generally go? Right now it is in my user controller's Register POST, which from your comment, sounds like the wrong place.

    Thanks

  • Vladimir Kelman said

    Blacnet,
    I think, regardless of is it ASP.NET MVC application or old style ASP.NET application or something else, business logic should be separated in another Tier. MVC part is actually a presentation layer, not a business layer. (Forget about Big Ball of Mud http://www.laputan.org/mud/ approach Microsoft was constantly teaching you.)
    We're building our "classic" ASP.NET using a modified N-Tier approach by Imar Spaanjaars http://goo.gl/wddFx

  • Rachel said

    Blachniet,

    Generally, yes, keep your controllers short and sweet. Logic can go in separate tiers that you might break out in your model. (for example, having a model and service layer).

    Some code, of course, can go in the controller, the situation is always dependent on the app requirements and how much code you feel comfortable putting there.

    Many folks use a repository (using the repo pattern) or create a BLL/DAL layer in addition to the model. these BLL/DALs would do the access to the data.

    as Vladimir mentioned, you want to stay away from the "big ball of mud", and MVC is a presentation layer. You'll be adding the functionality to your business layer.

  • William said

    Rachel,

    I am also new to MVC. In your example markup of the view, you seem to use model (lower case m) interchangeably with Model (upper case M).

    Thanks for the great article.

  • Chris Hohmann said

    @William: The instances where "model" is used represent lambda expressions. It doesn't actually matter what is used in that case, it's just a place holder. The following code would have worked just as well:

    @Html.LabelFor(m => m.Customer.FirstName)

  • Phil said

    Thanks. This article is nice and finally view model makes sense. I keep reading post talking about it but no one broke it down to it core. You rock!

  • PeterS said

    Nice article.
    Just now trying to get to grips with MVC.

    But, shouldn't this line:
    @Html.DropDownList("state", new SelectList(StatesDictionary.StateSelectList,

    Really be:
    @Html.DropDownList("state", new SelectList(Model.States.StateSelectList,

    Or am I missing something?

  • Rachel said

    PeterS,

    Yes! I had two examples for different posts with really similar code and big copy/paste error there.

    @Html.DropDownList("state", new SelectList(Model.States.StateSelectList,

    That is the correct code.

  • Brad said

    Better yet, create the SelectList in the controller and assign it to a view model property. I've greatly improved my view template readability/maintainability with this approach.

  • Robert Mayer said

    Being new to ASP.NET MVC, I've been struggling to get my head around how to work with edit views and dropdownlist controls. I've seen examples where the list data is passed from the controller via ViewBag, but the ViewModel approach you describe just feels like the right way to me.

    Your example in the ProductsController uses the ViewBag, while the CustomersController follows the ViewModel approach. However, there is no code in the CustomersController to handle the form post from the edit view. I came across one other example from a Microsoft guy named Rick Anderson, http://blogs.msdn.com/b/rickandy/, but my efforts to mimic that behavior have failed. The loading of the form fields, including the dropdownlist, in edit view works fine, but for some reason those field values never make it back into the model piece of my ViewModel during the post.

    Short of providing you my code, does any of this sound familiar enough to suggest a likely culprit?

  • Rachel said

    Robert,

    Make sure the form fields are rendering inside the FORM tags in the view.

    Make sure HttpPost is on the controller

    Code in controller will be something like this (can use either arg, no need for both).

    [HttpPost]
    public ActionResult Update(TheViewModel viewModel, FormCollection form)
    {
    //access through form or viewmodel passed in from view
    }

    Default MVC model binding will map form fields to properties in the viewmodel and/or built-in FormCollection. You should be able to inspect either arg w/debug tools in the controller.

    That should get you started.

  • Dave said

    So...I had a discussion at work yesterday about where our BL is (new to this company) in our app's solution. There isn't one. They are putting the BL logic in the ViewModel classes. Personally I still want a BL layer that the ViewModel is going to use just because you're always going to need a BL in any application and should for obvious reasons. So where in your discussion do you mention the BL? What's your take on that? I don't think it's good to put a lot of workflow and BL related code in a ViewModel class. BL code should be abstracted always into a BL project and their own domain related classes that talk to your DL.

  • Rachel said

    Dave,

    I agree there should be a separate BL rather than all in the ViewModel. ViewModels are for exposing the data a particular way for the view to consume.

    Check out the first illustration in this post - the top blocks could be the Model or BL architected more granularly than here (I was using a basic sample in the post).

  • Leon Reeder said

    Rachel,

    Thank you so much for your blog and this article. You dev-evangelists are a lifesaver for newbees to MVC. I think Barts' post ("Bart Czernicki said on Jul 19 2011 at 1:32 PM") would make a great article to demonstrate "write-once" for everything else but the presentation layer.

  • David Coyer said

    Echoing the thanks for this article; it is the single best one I have read for beginners (that's me) on how to use View Models.

    I got the view model working and I can make it work in my view if I manually code the view. However, I can't get scaffolding to work. If I try to use the CRUD automatic scaffolding, I get some error about the view model not being part of the dbcontext and it can't be added...

    If I build the controller actions first, then choose to add a view from the method (let's say the edit method), the view gets generated, using the correct @model declaration, but the view is empty. Just a heading and a submit button, like it can't read the interface of the view model. Has anyone run into problems like that?

    Thanks again for such a great site!

  • KB said

    Hi,
    I tried to open the solution using VS 2010 Ultimate and visual studio gives the following error. Any thoughts?

    I have ASP.NET MVC4, Windows Phone SDK also on my machine. I also have VS 2011.

    ---------------------------
    Microsoft Visual Studio
    ---------------------------
    The project file 'C:\Users\kb\Desktop\FourthCoffee-ViewModels\FourthCoffee.Web\FourthCoffee.Web.csproj' cannot be opened.

    The project type is not supported by this installation.
    ---------------------------
    OK Help
    ---------------------------

  • Rachel said

    David,

    The scaffolding errors on vms/models depending on if you do or don't have the dbcontext in the code already and choose to use it or not. (it's all setting dependent). Scaffolding looks for that context to match it with EF but you can the use empty CRUD template instead of read/write or CRUD with EF options, then you won't see that error.

    Yes, I've had that happen a fiew times with the viewss. Double check the scaffold type drop down, it tends to get stuck w/ViewModels. To work around I just scaffold from a similar model and code the rest.

  • Rachel said

    KB,

    That error means there is something missing (as you suspect). It could be a few things:

    -language, do you have C# installed or just vb?
    -Do you have MS Visual Web Developer 2010 installed? Check help->about
    -It's ASP.NET MVC 3, so it should open in 4, you should be OK there
    -The phone tools won't make a difference
    -I don't think VS11 is an issue. (here's where I say 'works on my machine' w VS2010 & VS11). :)

  • Walid Ward said

    Very Nice Article
    Thanks Rachel for that great effort, really its useful (the best article about ViewModel i red till now), but in MVC i understood the concept but when work with multi tear project and you have business layer and data access layer , i'm little bit lost where with Edit actions , do you recommend some project in codplex have well architecture (N-Teir) project like (Prodinner, contoso university )

  • Rachel said

    Walid,

    Yes, check out silk.codeplex.com - this is Project Silk, an MVC project that is about demonstrating best practices in MVC, JavaScript, and NTier apps.

    Jon Galloway's MVC Music Store is quite good too.

    Rach

  • Jerry said

    Hello.

    I'm sorry I'm getting to the party a little late, but, as with other comments, I'm new to MVC, ViewModels, etc. and trying to get a handle on things.

    In reference to the comment - Bart Czernicki said on Jul 19 2011 at 1:32 PM (regarding creating interfaces to inject different ViewModels for different data/Views) - what would that code look like? How would one implement such an idea?

    We're discussing a new MVC app that will have different views for desktop, mobile, and/or tablet browsers and would like to use a different ViewModel for each option.

    Thanks!

  • Rachel said

    Jerry,

    Check out this article: http://www.codeproject.com/Articles/207820/The-Repository-Pattern-with-EF-code-first-Dependen

    That should give you a good start.ViewModels are just a representation of data so you could get away with just one and multiple views instead (there are a few ways to do this)

  • Osmond said

    Hi,

    Nice article. Like some of the other I'm new to MVC and was wondering how you would assign the SelectList in the controller and assign it to a view model property and then display that in the view.
    For one of my views I require setting around 9 dropdownlist. What would be the best approach - setting these in the controller or in the view as per your example?

    some quick code example would be very helpful.

  • Rachel said

    Osmond,

    The SelectList is part of the ViewModel so that's returned with the rest of the ViewModel.

    public ActionResult Edit(int id) { Customer customer = context.Customers.Single(x => x.Id == id); var customerViewModel = new CustomerViewModel(customer); return View(customerViewModel); }

    <div class="editor-field"> @Html.DropDownList("state", new SelectList(StatesDictionary.StateSelectList, "Value", "Text", Model.Customer == null ? "" : Model.Customer.State)) @Html.ValidationMessageFor(model => model.Customer.State) </div>


    In the view, the selectlist is a property of the viewmodel and the code is as follows:

Comments have been disabled for this content.