Rafael Merlin
Rafael Merlin

Reputation: 2767

Persisting Dropdown information when ModelState is not valid

I'm having some issues with my DropDownLists, because when I post the information and my Model is not valid it comes back "empty" to the page triggering an error exactly like this question.

I've used the solution proposed there and it fixed my problem. Anyway, I wanted to avoid querying the database every time my ModelState is not valid and I came with this approach. I would like to know if it is valid or if there are better ways to do it now, considering that instead of MVC2 (which was the MVC version from the question) I'm now using MVC 5, maybe they added something new to tackle this.

What I've done was to use the TempData to persist the information when my model is not valid.

public class ViewModel
{
    [DisplayName("Project")]
    public int ProjectID { get; set; }
    public List<SelectListItem> Projects { get; set; }

    //Other fields
}

Now my Create() Action (that populates Projects)

[HttpGet]
public ActionResult Create()
{
    ViewModel vmodel = new ViewModel();
    vmodel.Projects = db.GetProjects(User.Identity.Name).Select(x => new SelectListItem { Text = x.Description, Value = x.Id }).ToList();
    TempData["Projects"] = vmodel.Projects;

    return View(vmodel);
}

And my post would be like this:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ViewModel vmodel)
{
    //Clear TempData (in theory will clear my tempdata when read, so if this controller redirects to another action my tempdata will be clear)
    List<SelectListItem> projects = (TempData["Projects"] as List<SelectListItem>);

    if (ModelState.IsValid)
    {
        //...
    }

    //If it got here it's going back to the screen.
    //Repopulate the TempData (allowing it to exist one more trip)
    TempData["Projects"] = projects;
    vmodel.Projects = projects

    return View(atendimento);
}

Is this approach a good one? Is there a better way to achieve that without querying the database every single time?

Thanks a lot!

Upvotes: 3

Views: 2910

Answers (2)

Oliver
Oliver

Reputation: 9002

Personally, I wouldn't worry about querying the database each time for this kind of operation.

What if projects are added/deleted? This could be the reason the save failed (selected project deleted) and the user would never realise it.

I usually write a method to populate all of my view model's SelectListItems and then use this in my Get and in my Post if the validation fails.

Upvotes: 1

Shyju
Shyju

Reputation: 218832

You don't need to use TempData at all as you have a property in your view model to hold the dropdown items.

public ActionResult Create()
{
    ViewModel vmodel = new ViewModel();
    vmodel.Projects = GetProjects(); 
    return View(vmodel);
}
private List<SelectListItem> GetProjects()
{
     return db.GetProjects(User.Identity.Name)
                 .Select(x => new SelectListItem { Text = x.Description,
                                                   Value = x.Id }).ToList();   
}

And in the view

@Html.DropDownListFor(s=>s.ProjectID,Model.Projects)

And in your HttpPost action, If ModelState is not valid, Reload the Projects collection again (because http is stateless)

if(ModelState.IsValid)
{
  // to do :Save and redirect
}
model.Projects = GetProjects(); 
return View(model);

You may cache the Projects so that you do not need to hit the database every time, if you are too much worried about performance.

Upvotes: 6

Related Questions