Maintaining ViewModel fields with default model binding and failed validation
- by TonE
I have an ASP.Net MVC Controller with a 'MapColumns' action along with a corresponding ViewModel and View.
I'm using the defaultModelBinder to bind a number of drop down lists to a Dictionary in the ViewModel. The view model also contains an IList field for both source and destination columns which are used to render the view.
My question is what to do when validation fails on the Post call to the MapColumns action?
Currently the MapColumns view is returned with the ViewModel resulting from the default binding. This contains the Dictionary values but not the two lists used to render the page. What is the best way to re-provide these to the view?
I can set them explicitly after failed validation, but if obtaining these values (via GetSourceColumns() and GetDestinationColumns() in the example) carries any overhead this doesn't seem ideal. What I am looking for is a way to retain these lists when they are not bound to the model from the view.
Here is some code to illustrate:
public class TestViewModel
{
public Dictionary<string, string> ColumnMappings { get; set; }
public List<string> SourceColumns;
public List<string> DestinationColumns;
}
public class TestController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult MapColumns()
{
var model = new TestViewModel;
model.SourceColumns = GetSourceColumns();
model.DestinationColumns = GetDestinationColumns();
return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MapColumns(TestViewModel model)
{
if( Validate(model) )
{
// Do something with model.ColumnMappings
RedirectToAction("Index");
}
else
{
// Here model.SourceColumns and model.DestinationColumns are empty
return View(model);
}
}
}
The relevant section of MapColumns.aspx:
<%
int columnCount = 0;
foreach(string column in Model.targetColumns)
{%>
<tr>
<td>
<input type="hidden" name="ColumnMappings[<%= columnCount %>].Value" value="<%=column %>" />
<%= Html.DropDownList("ColumnMappings[" + columnCount + "].Key", Model.DestinationColumns.AsSelectItemList())%>
</td>
</tr>
<%
columnCount++;
}%>