1

I have a model that has a PersonId int, and also, a List<SelectListItems> People for people. The UI allows me to select a person from the drop down model, and save the value into PersonId.

That works, but if there is an error in my ModelState, caused by another field, I do this:

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

Problem is, the object holding the list of people is NULL in the returned model. Do I really need to repopulate it from the database again, or can this somehow be 'stored', and only populated when I initially create the view?

John H
  • 14,422
  • 4
  • 41
  • 74
Craig
  • 18,074
  • 38
  • 147
  • 248

2 Answers2

1

Yes. You need to load the SelectView again. Only values in your inputs are posted (hidden or normal ones)... so anything to be posted must be in those inputs.

If you want to avoid going to the database... you should cache that list. Here you can see an example for the caching logic: https://stackoverflow.com/a/349111/7720

Community
  • 1
  • 1
Romias
  • 13,783
  • 7
  • 56
  • 85
1

Yes, you need to repopulate it. Only the selected value of a dropdown list is sent via a form. It's perfectly normal to do this. In general, if you have data available on the server, it always makes sense to cache it/query for it again, rather than trusting any input from a user.

To conveniently build SelectLists, rather than using SelectListItem, I use a method on a base controller:

[NonAction]
public SelectList BuildSelectList<TSource>(IEnumerable<TSource> source,
    Expression<Func<TSource, int>> valueKey, Expression<Func<TSource, string>> textKey,
    object selectedValue = null)
{
    var selectedValueKey = ((MemberExpression)(MemberExpression)valueKey.Body).Member.Name;
    var selectedTextKey = ((MemberExpression)(MemberExpression)textKey.Body).Member.Name;

    return new SelectList(source, selectedValueKey, selectedTextKey, selectedValue);
}

Note the use of NonActionAttribute, which is used to indicate that a public controller method is not an action method.

Then in the controller, it's easy to build any list again, without polluting the actions too much. For example:

[HttpPost]
public ActionResult Index(SomeViewModel model)
{
    if (ModelState.IsValid)
        return RedirectToAction("Success");

    // The model wasn't valid, so repopulate the dropdown
    model.People = BuildSelectList(db.People, m => m.Id,
                                    m => m.Name, model.PersonId);

    return View(model);
}

You could do something similar for SelectListItem, rather than manually rebuilding your lists each time.

John H
  • 14,422
  • 4
  • 41
  • 74