Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC - Partially updating model from view

I just wondered how people were approaching this situation. It's something that seems like a weak point in my usage of MVC with ORMs (NHibernate in this case)...

Say you have a fine-grained and complicated entity in your model. You will likely have an admin page to manage objects of this type. If the entity is complicated, it is unlikely that you will be modifying the whole entity in one form. You still need to pass the relevant properties to the view, and incorporate changes to those properties in the model when the view returns them.

What does anyone do in this situation?

  • Create a view model which is (or contains) a subset of the entities properties. Pass this to and from the view. In 'edit' action method in controller, get the object from repository, go though all the properies in the ViewModel and apply them to the Model object (model.a = viewmodel.a, modelb = viewmodel.b). This seems the obvious sensible route, but generates a lot of tedious plumbing code. Also this complicates validation a bit.

  • Something else?

I've looked briefly at automapper - but this doesn't seem to fit the bill exactly, maybe I'm wrong?

Thanks.

like image 613
UpTheCreek Avatar asked Nov 26 '09 12:11

UpTheCreek


People also ask

Can we use model in partial view?

To create a partial view, right click on Shared folder -> select Add -> click on View.. Note: If the partial view will be shared with multiple views, then create it in the Shared folder; otherwise you can create the partial view in the same folder where it is going to be used.

Does Model Update view in MVC?

In a web MVC the model does not update any view! The model should know nothing about the external world. Instead: (a) Controller updates the model and then reads the data from it, in order to pass it further to the view for displaying it; or (b) Controller updates the model.


2 Answers

This sounds like the perfect scenario for automapper. You create a view model class which contains a subset of the fields or your real model, and you let AutoMapper take care extraccting values from the domain model object into your view model object. What issues are you having with this approach?

Consider this example:

Here is your domain model and your view model

public class Person
{
    public string FirstName
    { get; set; }

    public string LastName
    { get; set; }

    public string HomeNumber
    { get; set; }

    public string Address1
    { get; set; }

    public string Address2
    { get; set; }
}

public class PersonViewModel
{
    public string FirstName
    { get; set; }

    public string LastName
    { get; set; }

    public string HomeNumber
    { get; set; }
}

Here is your mapping, you have to create a mapping in both directions from dm->vm and vm->dm.

From what I've seen when using Automapper is that if you map from object A to B and B has a property which A doesn't have, it will be reset. So when I create the map I direct it to ignore those missing properties. I'm not a Automapper expert so I may be using it wrong.

Mapping

Mapper.CreateMap<Person, PersonViewModel>();
// Automapper will reset values in dest which don't exist in source, so be sure to ignore them!
Mapper.CreateMap<PersonViewModel, Person>()
    .ForMember(dest => dest.HomeNumber, opt => opt.Ignore());

Finally usage:

Person p = new Person()
{
    FirstName = "First",
    LastName = "Last",
    Address1 = "add 1",
    Address2 = "add 2"
};

PersonViewModel pvm = Mapper.Map<Person, PersonViewModel>(p);
// Map to a new person
Person p2 = Mapper.Map<PersonViewModel, Person>(pvm);

// Map to the existing person just to update it
Person p3 = new Person()
{
    HomeNumber = "numberHere"
};

// This will update p3
Mapper.Map<PersonViewModel, Person>(pvm, p3);

Because of the exclusion, this is obviously less than ideal, but much better than manually doing the whole thing.

like image 188
Sayed Ibrahim Hashimi Avatar answered Sep 24 '22 03:09

Sayed Ibrahim Hashimi


  1. Have your view model map one-to-one with your domain model.

  2. Specify Model as argument for the routeValues as below. This means your view model will be initialized with the values from the domain model. Only the sub set of fields in the form will be overwritten in the resulting personViewData.

Update View:

@model ViewModel.PersonView

@using (Html.BeginForm("Update", "Profile", Model, FormMethod.Post))
{
  ...Put your sub set of the PersonView fields here
}

ProfileController:

public ActionResult Update(string userName)
{
    Person person = _unitOfWork.Person.Get().Where(p => p.UserName == userName).FirstOrDefault();
    PersonView personView = new PersonView();
    Mapper.Map(person, personView);

    return View(personView);
}

[HttpPost]
public ActionResult Update(PersonView personViewData)
{
   Person person = _unitOfWork.Person.Get().Where(p => p.UserName == personViewData.UserName).FirstOrDefault();
   Mapper.Map(personViewData, person);
   _unitOfWork.Person.Update(person);
   _unitOfWork.Save();

   return Json(new { saved = true, status = "" });
}
like image 20
Troels Wittrup Avatar answered Sep 22 '22 03:09

Troels Wittrup