Adrian Grigore

ASP.NET MVC Tip #1: Using Custom ViewModels with POST action methods

In ASP.NET MVC, LINQ on April 18, 2009 at 13:44

One of the top good practices for ASP.NET MVC is not to use the ViewData Dictionary, but to put your data in a strongly typed ViewModel instead. Many people seem to be using Linq to SQL entities as a ViewModel, because it’s a very comfortable approach. But what do you do if your view should contain data that is not included in any of your linq entities? Scott Gu’s chapter 1 preview of his upcoming ASP.NET MVC book recommends using a custom-shaped ViewModel for those cases.

One thing that still puzzled me after reading and trying to follow the chapter was how to create complex custom ViewModels (as opposed to putting all data in the ViewData dictionary or using vanilla Linq to SQL entities). Scott mentions on page 107 of the book that you “might have the action method update a ViewModel object with the form-posted data, and then use the ViewModel instance to map or retrieve an actual domain model object”, but there is no actual example of how this would look in source code.

For example, let’s assume you have an edit action method with the following view:

So your edit form displays data coming from a Linq entity representing a customer (FirstName, LastName, Email, Country, AccountType), but you also need SelectLists to populate the Country and AccountType DropDownLists.

The simple, but untyped approach: the ViewData dictionary

The simplest approach is to use the Customer Linq entity as ViewModel and to put the two SelectLists in the ViewData dictionary. This works fine, but I dislike the fact that there is no type-safety for the two SelectLists. Instead you have to use a type cast from object to SelectList:

Country

The fully typed approach: Custom ViewModels

So you want fully typed model data, but the linq entity does not hold all the data you need to render the view. Since you don’t want to clutter my Customer Linq to SQL entity with two properties that return the two SelectLists, the only alternative to get a fully typed view is to use a custom ViewModel:

 

public class CustomersFormViewModel
{
public SelectList AccountTypes { get; set; }
public SelectList Countries { get; set; }
public Customer Customer { get; set; }
}

 

Note that the ViewModel is not a mere Linq entity anymore. Instead, it contains the one Customer Linq entity instance, plus the two SelectLists for my DropDownLists.

Now you can implement your view without using any typecasts:

</pre>
FirstName:

LastName:

Email:

Country
<pre>

AccountType:

I was wondering if the MVC framework can automatically map the form values and reconstruct a CustomersFormViewModel. In other words, can I simply implement a strongly typed POST edit action method and receive a fully populated ViewModel as a parameter like this:

</pre>
//POST: /CustomersController/Edit

[AcceptVerbs(HttpVerbs.Post)]
 public ActionResult Edit(CustomersFormViewModel model)
 {
 //validate data, save customer, handle validation errors...
 }
<pre>

The answer is yes! The MVC framework even maps object hierarchies of any depth as ViewModels.

How not to do it

This is all really easy, but still there were few things that threw me off at the beginning.

The ViewModelBinder only maps properties, not public fields. A ViewModel like this cannot be mapped:

 

</pre>
public class CustomersFormViewModel
 {
 public electList AccountTypes;
 public SelectList Countries;
 public Customer Customer;
 }
<pre>

The names of your POSTed form values must match the object hiearchy you want them to be mapped to. Something like this will not work:

</pre>
FirstName:
<pre>

In hindsight both restrictions are perfectly logic and reasonable, but it’s easy enough to do it wrong nevertheless and also quite difficult to figure out what went wrong if binding does not work. Hopefully this article will help you avoid any problems with using Custom ViewModels with POST action methods.

kick it on DotNetKicks.com

  1. Good post, I’d add that you should never use you linq to sql objects in your views, but always convert them to something that is in related to you data access strategy
    This will give you a few more benefits:
    1 – you are not tied to a specific implementation of your dal
    2 – you can have a different hierarchy of objects, instead of being tied to the table approach
    3 – you can just create your objects when you are testing (LINQtoSQL objects are not POCO objects, or, at least, this is difficult to accomplish)

    And then, you rarely have to use the same objects both in your views and in your BL.

  2. Great Post. I too think strongly typing is the way to go… however for this simple example with a select list or two, I don’t necessarily see the benefit of writing more code to handle this.

  3. I tried the drop down list and the model tries to map it to an enumerable type when I was hoping it would map the selected value to the property name given to the drop down list as in your example.

  4. Hello Adrian,

    Interesting post. I tried to implement the Custom View Model on my project in a similar context and it does not work. I don’t know what i do wrong.

    If you can help me these are below the lines of code :

    The CustomView Model :
    ————————————————–
    public class MeditationsFormModel : IFormModel
    {
    public Meditation Meditation { get; set; }
    public SelectList Types { get; set; }
    }

    The post action in the controller :
    ———————————————

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult ValiderEdit(MeditationsFormModel m)
    {
    //
    }

    The dropdownlist in the view :
    ——————————————————

    Type

    Thanks.

  5. @Anonymous: I’m sorry, but I can’t follow your explanation. Perhaps you could post some code?

    @Philippe: The code you posted looks incomplete. Also, what exactly is going wrong?

  6. great article, does this stuff also works with a MultiSelectList ?

  7. @BSvanVeen: I haven’t used MultiSelectList yet, but I’m sure it does. Basically you can put whatever you want into your ViewModel. Even your own custom classes, which could be evaluated by your own custom HtmlHelper extension methods.

  8. I tried it on a multislect and it worked well thanks

  9. All my objects within my model was null also. Tried everything.

  10. What if I want to save all options in the Select? I.E. A user drag and drop a bunch of objects into a basket. I need to save all these objects into database in order.

  11. @X: Phil Haack has written an article about this: http://haacked.com/archive/0001/01/01/model-binding-to-a-list.aspx

  12. FYI:

    That link to Phil’s site in Adrian’s previous post is invalid… here is the correct link:

    “Model Binding To A List”
    http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

  13. I have found that Html.EditorForModel() doesn’t work in this case; the SelectLists don’t render. If I manually bind the SelectLists using Html.DropdownListFor, it does work. Have you found a way to use EditorForModel() in this case?

  14. Sorry, I haven’t had the time to have a look at MVC2’s templates yet. I’ve only just migrated my current MVC project to the MVC 2 codebase this week.

  15. To Philippe:
    You should code like this:

    Not

    because the ViewModelBinder will bind the form to a object Customer, not CustomersFormViewModel.

  16. To Philippe:
    Sorry, the code i post before invisible
    you should use “FirstName” , not “Custom.FirstName”

  17. @Philippe: It doesnt appear as though youre setting your properties. Try this:

    public class MeditationsFormModel : IFormModel
    {
    public Meditation Meditation { get; private set; }
    public SelectList Types { get; private set; }

    public MeditationsFormModel(Meditation meditation)
    {
    Meditation = meditation;
    Types = new SelectList( ::whatever your IEnumerable type is:: , ::whatever your selected value should be:: );
    }
    }

  18. you can use ValueInjecter http://valueinjecter.codeplex.com/ for mapping ViewModel to/from Entities, it’s very good for this purpose

  19. @Adrian:

    Nice article. I was stuck on this for a while this morning. Just a quick point regarding your last post. I have looked at automapper, and it does indeed look nice. But does TryUpdateModel(,) in MVC 2 not do the same thing?

    For example, I could easily map a FormCollection passed in as a parameter to an Action and map it to a Entity using this TryUpdateModel.

  20. @Ross: Yes, but TryUpdateModel will not update a viewmodel from an DTO when constructing the view (GET request).

    Also, I don’t like working with FormCollections for POST requests, because it makes unit testing less intuitive. Even though I’ve writting a helper method that creates a fake http context from a model for unit testing, I still prefer custom ViewModels as POST action method parameters

  21. Hi,
    I just read this post about the viewmodel, while searching for a solution to my problem.
    I have a simple class for testing called accountModel

    Public Class AccountModel
    Public Firstname as string
    Public Lastname as string
    End Class

    Then I have class AccountViewModel
    Public Class AccountModel
    Public Account as AccountModel
    Public Sub New()
    End Sub

    Public Sub New(byval account as AccountModel)
    Account = account
    End Sub
    End Class

    When i use my accountviewmodel in my controller GET controller action, i have no problem accessing the values from my “AccountModel” like Model.Account.Firstname.
    But when I POST my form, i alwas get this error “No parameterless constructor defined for this object.”
    The actions does the following:
    Public Function Create(byval account As AccountModel) as ActionResult
    But when I use the action like this it works
    Public Function Create(byval model as AccountViewModel) as ActionResult

    Does someone tell me why i’m getting this exception?
    Thanks

  22. Great article!

    However after reviewing your article and exercises and also after trying to follow the Nerdinner tutorial I am still wondering two questions:

    1) What are the strategies-guidelines and how to define a view-model and/or associated view that needs to render in 1 specific view more than 1 entity? For instance suppose that I need to render in just one view the details of a student and the details of a room which will be used for the student classes ( => room details are: area location, type of room (lab, auditorium or normal), max_capacity ).

    2) What are the strategies-guidelines and where would you define specific UI logic that is based value data.
    For this question consider 2 scenarios:
    a) When you need to draw/display certain controls/widgets only if one of the attibutes of a queried entity has certain value.
    b) When you need to draw/display certain controls/widget or screen area inmediatelly, if one of the values one widget of the same view gets its data from a previous input of the user.

    Regards
    Carlos

Leave a comment