Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using view models in ASP.NET MVC 3

I'm relatively new to view models and I'm running into a few problems with using them. Here's one situation where I'm wondering what the best practice is...

I'm putting all the information a view needs into the view model. Here's an example -- please forgive any errors, this is coded off the top of my head.

public ActionResult Edit(int id)
{
    var project = ProjectService.GetProject(id);

    if (project == null)
        // Something about not found, possibly a redirect to 404.

    var model = new ProjectEdit();
    model.MapFrom(project); // Extension method using AutoMapper.

    return View(model);
}

If the screen only allows editing of one or two fields, when the view model comes back it's missing quite a bit of data (as it should be).

[HttpPost]
public ActionResult Edit(int id, ProjectEdit model)
{
    var project = ProjectService.GetProject(id);

    if (project == null)
        // Something about not found, possibly a redirect to 404.

    try
    {
        if (!ModelState.IsValid)
            return View(model) // Won't work, view model is incomplete.

        model.MapTo(project); // Extension method using AutoMapper.
        ProjectService.UpdateProject(project);
        // Add a message for the user to temp data.

        return RedirectToAction("details", new { project.Id });
    }
    catch (Exception exception)
    {
        // Add a message for the user to temp data.

        return View(model) // Won't work, view model is incomplete.
    }
}

My temporary solution is to recreate the view model from scratch, repopulate it from the domain model, reapply the form data to it, then proceed as normal. But this makes the view model parameter somewhat pointless.

[HttpPost]
public ActionResult Edit(int id, ProjectEdit model)
{
    var project = ProjectService.GetProject(id);

    if (project == null)
        // Something about not found, possibly a redirect to 404.

    // Recreate the view model from scratch.
    model = new ProjectEdit();
    model.MapFrom(project); // Extension method using AutoMapper.

    try
    {
        TryUpdateModel(model); // Reapply the form data.

        if (!ModelState.IsValid)
            return View(model) // View model is complete this time.

        model.MapTo(project); // Extension method using AutoMapper.
        ProjectService.UpdateProject(project);
        // Add a message for the user to temp data.

        return RedirectToAction("details", new { project.Id });
    }
    catch (Exception exception)
    {
        // Add a message for the user to temp data.

        return View(model) // View model is complete this time.
    }
}

Is there a more elegant way?

EDIT

Both answers are correct so I would award them both if I could. The nod goes to MJ however since after trial and error I find his solution to be the leanest.

I'm still able to use the helpers too, Jimmy. If I add what I need to be displayed to the view bag (or view data), like so...

ViewBag.Project= project;

I can then do the following...

@Html.LabelFor(model => ((Project)ViewData["Project"]).Name)
@Html.DisplayFor(model => ((Project)ViewData["Project"]).Name)

A bit of a hack, and it requires the domain model to be decorated with System.ComponentModel.DisplayNameAttribute in some cases, but I already do that.

I'd love to call...

@Html.LabelFor(model => ViewBag.Project.Name)

But dynamic causes a problem in expressions.

like image 490
Adam Boddington Avatar asked Mar 15 '11 01:03

Adam Boddington


People also ask

What is the use of ViewModel in ASP.NET MVC?

In ASP.NET MVC, ViewModel is a class that contains the fields which are represented in the strongly-typed view. It is used to pass data from controller to strongly-typed view.

Is it OK to use entities for view models in ASP.NET MVC?

In ASP.NET MVC, ViewModels are used to shape multiple entities from one or more models into a single object. This conversion into single object provides us better optimization.

Can we use 2 models in a view?

You can use multiple models in a single view by creating a common model for all the models that are to be used in a single view. To achieve this, refer to the following steps. First, create a new model (common for all models) and refer all other models that are to be used in the same view.

How do you use view models?

There are three steps to setting up and using a ViewModel: Separate out your data from your UI controller by creating a class that extends ViewModel. Set up communications between your ViewModel and your UI controller. Use your ViewModel in your UI controller.


1 Answers

After some trial-and-error (aka code it, then hate it) learning, my currently preferred approach is:

I use view-models only to bind input fields. So in your case, if your view is only editing two fields, then your view-model will only have two properties. For the data required to populate the view (drop-down lists, labels, etc), I use the dynamic ViewBag.

I believe that displaying the view (i.e. populating anything the view needs to display), and capturing the posted form values (binding, validation, etc) are two separate concerns. And I find that mixing the data required to populate the view with that which is posted back from the view gets messy, and creates exactly your situation more often than not. I dislike partially populated objects being passed around.

I’m not sure how this plays out with Automapper (for mapping the domain object to the dynamic ViewBag) though, as I haven’t used it. I believe it has a DynamicMap method that may work? You shouldn’t have any issues auto-mapping the posted strongly-typed ViewModel onto the Domain object.

like image 79
MJ Richardson Avatar answered Oct 11 '22 21:10

MJ Richardson