views:

490

answers:

7

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.

+1  A: 

What if you have full model but each page uses and updates only the required part? Then you update the business model using complete view data at the last page.

queen3
Thanks. I think you are suggesting some kind of multipage 'wizard' style form? I'm not really talking about situations where I have to update the whole model in smaller chunks, but rather just the details of passing a of the model for update.
UpTheCreek
No i don't think he is. I think he's suggesting not to use a viewmodel. Simply pass the complete model and have the action only change/update the properties that you need to. Simplify the process by removing the extra plumbing of the VM object.
cottsak
So, what about the bit 'update the business model using *complete view data at the last page*' then?
UpTheCreek
+1  A: 

I use a view model and you are completely right it does lead to some boring left hand right hand code. From what I have gathered AutoMapper alleviates some of that code.

Aaron
Yeah, I have the feeling that this is the way it has to be. You never know, someone might have some ingenious trick though...
UpTheCreek
+1  A: 

y, viewmodel + automapper.

ps. a complicated model, tends to ... complicate everything - is it really worth it in your scenario?

eglasius
+3  A: 

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.

Sayed Ibrahim Hashimi
Thanks Sayed, this is fine for 'view only' situations. But as I understand it automapper is unable to map back to the domain model object given edited values from the view (for instance in an 'edit' admin form situation). Maybe I am wrong?
UpTheCreek
I just added an example here for you.
Sayed Ibrahim Hashimi
Thanks for the detailed examples, looks like there is more to automapper than I had realised. I'll do a bit more research.
UpTheCreek
+1  A: 

Why don't you use TryUpdateModel with the form collection.

If your view is editing a person

public class Person
{
    public string ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Address { get; set; }
}

And your view is only editing first name and last name, you can do this:

public ActionResult Action(FormCollection form)
{
    Person personToUpdate = Repository.GetPerson(form["ID"]);

    TryUpdateModel<Person>(personToUpdate, form);

    Repository.Update(personToUpdate)

    return View();
}

That will only update Person with the items that a part of the form collection. If you don't want a field updated, don't submit it with the form.

Odd
I speak without certainty and would love to get some feedback. I think you don't want to do this because 1. It's less strongly typed, 2. it can't be combined with a custom model binder. Levi has explained somewhere that updatemodel was only intended for updating the viewmodel.
Martin
A: 

Right now i´m working on a large project using S#arp Architecture and im also using the approach:

Model -> ViewModel -> Model

I use the ViewModel for the Binding part and Validations, the other approach is to use the Model Directly (with tryUpdateModel / UpdateModel which we used during the prototype develop) but for complex scenarios we end up handling special situation like SelectLists/Checkbox/Projections/HMAC Validations in a little ViewModel anyway and using a lot of Request.Form["key"] =( , the other drawback is handling the errors situations where you want to repopulate the form with the user input, i found it a little more complicated using the Model directly (using a ViewModel we take a lot of advantage of ModelState attempted value, saving us a couple of trips to the DB, anyone who have faced this scenario will know what i mean).

This approach is a bit time consuming, just like you said, you end up matching properties, but in my opinion is the way to go for complex forms.

It worth mentioning that we just use ViewModels for the Create/Edit scenarios, for almost everything else we use directly the model.

I have not use autommapers so far, but definitely i ll give it a try.

Omar
+1  A: 

I use a similar approach to yours (in my case Entity Framework) with Entity -> ViewModel -> View but only on views with "complex" entities that have either 1:M or M:M relationships. In most cases I took the low road and went for Entity->View when I have a simple entity.

My ViewModel is defined as Entity+supporting properties: SelectList or MultiSelectList and either a string or List<string>. I'll also use a ViewModel for instances where I have properties I need for the view but may not necessarily need in the entity (database).

Http Get controller methods are straightforward ActionResults with return View(repository.FetchNewViewModel()) for Create or repository.FetchModelById(id) for Edit. In both instances I'm initializing my entities before passing them to the view.

Create([Bind(Exclude = "Entity.EntityId")] ViewModel model) and Edit(ViewModel model) are the Http Post controller methods of Create and Edit. My Edit view has a hidden input field for EntityId to pass it back and forth.

By the time the Http Post method has the viewmodel, I lose all Entity.Relation and ViewModel.(Multi)SelectList values. I have to rebuild the object if I want my view to display properly: `

try
{
    var tags = model.TagIds; // List<string> or <int> depending on your Id type
    if (model.TagsList == null) // It will be
    {
        model.TagsList = _repository.FetchSelectedTags(tags); // Build a new SelectList
    }

    if (!ModelState.IsValid)
    {
        return View(model);
    }

    _repository.Add(model.Article, tags); // or Save() for Edit
}
catch
{
    return View(model); // Generally means something screwed in the repository class
}

return RedirectToAction("Index");

`

There is maybe 30% of my entity base using a ViewModel so I definitely only use it as needed. If you have complex views and model data in most instances you can probably break it down to smaller views.

w0rd-driven