In my MVC application, I normally have ViewModel for such actions as Edit, mainly for populating selection lists for the properties to modify. For example my Model looks like this:
class MyModel{
public int? NavPropID {get; set;}
public AnotherModel NavProp {get; set;}
...
}
Now I was wondering, how should I design my ViewModel to reference the NavProp.
class MyViewModel{
public int? NavPropID {get; set;}
public AnotherModel NavProp {get; set;}
...
}
Should I use both NavPropID and NavProp, or should I use only one (which?) in the ViewModel?
I wanted to use only NavProp at first, because it feels more natural to me (I feel it hides the database implementation detail and it is how I do in WPF and MVVM). But then in my Edit view, I have something like this:
@Html.DropDownListFor(model => model.NavProp,
new SelectList(Model.AllAnotherModels.Select(
i => new {Item = i, Text = i.Name}), "Item","Text"))
In the postback of Edit action, I can see that NavProp is not correctly bonded by the binder because the attemptedValue is of type string and the value is the class name of NavProp (which I guess means it uses ToString() method to post back to the controller). How can I make it work for the post back action?
I then tried to only have NavPropID in the ViewModel. But there are two problems: 1) it means I have to load the actual NavProp in the controller before I use AutoMapper to map back from ViewModel to Model. I feel that this is beyond the responsibility of a controller and 2) even if I load the actual property in the controller, I have some problem later when I am updating by calling DBSet.Attach().
So what is the best practice for the ViewModel to reference a navigational property in the Model?
UPDATE:
Here is my post back function of the Edit action. It is generic I think so I didn't paste it in the first place.
[HttpPost]
public ActionResult Edit(MyViewModel vm)
{
if (ModelState.IsValid)
{
... // I use Automapper to map back to Model and then
// use UnitOfWork and Repository to update the Model
}
}
And my ModelState.IsValid is false so it can't proceed. As I mentioned, I then checked the ModelState (a dictionary) and found out that NavProp is invalid and the attemptedValue is the class name while I think it should be the actual value of the property.