I'm working on refactoring a MVC project to clean up the controllers by using action filters to perform PRG (Post-Redirect-Get) when ModelState
is not valid, and storing ModelState
in TempData
during the redirect.
I have a view model that looks something like this:
public class GroupMember
{
public string UserId { get; set; }
public bool SendNotifications { get; set; }
}
public class Group
{
public int Id { get; set; }
public string Name { get; set; }
// ...
public List<GroupMember> Members { get; set; }
}
Controller actions:
[HttpGet]
[ImportModelState]
public ActionResult Create()
{
var model = new Group();
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
[ValidateModelState]
public ActionResult Create(
[Bind(Exclude = "Id")]
Group model)
{
// call service to save
// ...
}
The problem I'm having is with getting the collection Group.Members
to display correctly after a redirect. ModelState
is loaded 100% correctly, and works fine for the properties like Group.Name
, where the called to Html.EditorFor(...)
loads the data from ModelState
instead of using the (empty) model.
I have an editor template for GroupMember
(which works fine in all but the redirect scenario) and am displaying the collection using Html.EditorFor(model => model.Members)
, but apparently MVC does not check ModelState
in this case, so it always displays an empty list.
Is there a clean way to fix this? I think I should be able to store the number of members in a hidden field and check by hand to see if it has a value in ModelState
, but that seems like a clumsy workaround.
Edit:
The workaround I've come up with is to add a property NumMembers
to Group
, and insert the following into the Razor view:
@{
ModelState state;
var numMembers = ViewData.ModelState.TryGetValue("NumMembers", out state)
? int.Parse(state.Value.AttemptedValue)
: Model.Members.Count;
if (numMembers != Model.Members.Count)
{
Model.Members = Enumerable.Repeat(new GroupMember(), numMembers).ToList();
}
}
@Html.HiddenFor(model => model.NumMembers)
@Html.EditorFor(model => model.Members)
My javascript code which fixes the item indices for proper model binding updates the NumMembers hidden field before a post. Still hoping for a cleaner solution.