Archive

Posts Tagged ‘model’

ViewModels and updating model in ASP.NET MVC 3

August 27th, 2011

I recently been diving in to ASP.NET MVC 3 latley and quite quickley I ran in to an issue when using a ViewModel to update model data. You usually use a view model when combining data from different model objects to be shown in a view. You could put data in the ViewBag but I prefere using a ViewModel as you get much cleaner code and the view gets strongly typed to your model objects.
The issue is when posting data back to your controller the model binder can not map your view model object to your “real” model objects. The solution to this was much simplier then I expected.

If your not using a ViewModel you may have a action method looking something like this:

[HttpGet]
public ActionResult Edit(int id)
{
   MyModel model = context.MyModels.FirstOrDefault(m => m.Id == id);
   return View(model);
}

Then you would have a post method looking something like this:

[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
    MyModel myModel = context.MyModels.FirstOrDefault(m => m.Id = id);

    if (TryUpdateModel(myModel))
    {
        context.SaveChanges();
        return RedirectToAction("Index");
    }

    return View(myModel);
}

TryUpdateModel() will loop through the FormCollection values and map the posted data to the MyModel object.
If you look at the keys in the FormCollection you will see that they match your properties on the model. This is what makes the mapping possible.

But what now if you would like to use a ViewModel?
Here is an example view model that I have written.

public class UserProfileViewModel
{
    public UserProfile UserProfile { get; set; }
    public MembershipUser MembershipUser { get; set; }

    public UserProfileViewModel(string userName)
    {
        UserProfile = UserProfile.GetUserProfile(userName);
        MembershipUser = Membership.GetUser(userName);
    }
}

It’s a ViewModel containing a users profile and membership.
The UserProfile is a container of user information such as name, location, birthdate and so on.
The MembershipUser is a container for user name and email. The model is used in a view for displaying and edeting user information.

The edit get action would look something like this:

[HttpGet]
public ActionResult Edit(string userName)
{
    return View(new UserProfileViewModel(userName));
}

You would then have a post method looking something like this:

[HttpPost]
public ActionResult Edit(string userName, FormCollection collection)
{
    try
    {

        MembershipUser user = Membership.GetUser(userName);
        if (TryUpdateModel(user))
        {
            Membership.UpdateUser(user);
        }

        UserProfile profile = UserProfile.GetUserProfile(userName);
        if (TryUpdateModel(profile))
        {
            profile.Save();
        }

        return RedirectToAction("Index");
    }
    catch
    {
        return View();
    }
}

The problem now is that the FormCollection keys have the following values:
FormCollection key values

The model binder now has problem of mapping the FormCollection keys to the model object when calling TyrUpdateModel() as it can’t know what object to map with.
You can still user the TryUpdateModel() but use the overloaded method that takes a string prefix as an argument.
Here you can see how I have used the the method to update membership and userprofile opbjects:

if (TryUpdateModel(user, "MembershipUser"))

and

if (TryUpdateModel(profile, "UserProfile"))

This will enable the model minder to bind correctly.
It’s important to know that the string prefix should be the same as the ViewModels member name.
It will be clear to you when inspecting the content of the FormCollection keys in debug mode.

Development , , ,