I'm using MVC 4, .Net 4, and Visual Studio 2012.
I'm trying to use a fairly complex model with one of my views, and I'm having serious trouble getting it to bind properly.
The model wraps a Dictionary with integer Keys, and Values that are Lists of Lists of bools.
Basically, a search was done on items indicated by the integer, each item had several search terms, and for each of those terms we have a list of results. I display the results on a page, and have a checkbox next to each result. For each result, the user will indicate whether they want some stuff done by the next Action by checking the box.
At the moment, the checkboxes display properly, including the preset values from the controller, but when I press the submit button at the bottom of the form, I get this error:
Specified cast is not valid.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.InvalidCastException: Specified cast is not valid.
It appears to me to have something to do with using a Dictionary, which I'm told doesn't work well as a model. I may have to change to something else, but I'd rather not unless I absolutely have to. Seems like there might be an answer here somewhere: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx, or Checkbox list for complex type in asp.net mvc, or How to bind Dictionary type parameter for both GET and POST action on ASP.NET MVC, but I found those after I had the question all written up and I haven't figured it out yet, so maybe somebody can give me a hand.
Here's the top of the Stack Trace:
[InvalidCastException: Specified cast is not valid.]
System.Web.Mvc.CollectionHelpers.ReplaceDictionaryImpl(IDictionary`2 dictionary, IEnumerable`1 newContents) +131
[TargetInvocationException: Exception has been thrown by the target of an invocation.]
System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +92
System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +108
System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) +19
System.Web.Mvc.CollectionHelpers.ReplaceDictionary(Type keyType, Type valueType, Object dictionary, Object newContents) +178
Here's the model:
public class AutoResolveModel {
public Dictionary<int, List<List<bool>>> SelectedResults { get; set; }
public AutoResolveModel() {
SelectedResults = new Dictionary<int, List<List<bool>>>();
}
}
Since it might be relevant, here's the structure of ViewBag.iidToData, which holds the results to be displayed:
In the controller action:
var iidToData = new Dictionary<int, List<ItemSearchResult>>();
ViewBag.iidToData = iidToData;
Elsewhere:
public class ItemSearchResult {
public string C { get; set; }
public string S { get; set; }
public List<int> Ss { get; set; }
public List<int> Ks { get; set; }
}
Here's some relevant parts from the View with variable names changed to protect the innocent:
@model AutoResolveModel
@{
string machineID;
Submission subm;
tblSignatures sig;
ItemSearchResult result;
var dc = new CloudDataContext();
}
@using( Html.BeginForm( "MyAction", "MyController", new { p = (int?) ViewBag.l }, FormMethod.Post ) ) {
foreach( KeyValuePair<int, List<ItemSearchResult>> kv in ViewBag.iidToData ) {
<input type="hidden" name="@("SelectedResults[ " + kv.Key + " ].Key")" value="@kv.Key" />
ID = (
...
).Single();
<h3>Inventory Item @ID</h3>
for(int isr = 0; isr < kv.Value.Count(); isr++) {
result = kv.Value[ isr ];
<h4>Searched for @result.S from @result.C</h4>
<table border="0">
<tr><th>K</th><th>I</th><th>V</th><th>G</th><th>D</th><th>S</th><th>T</th></tr>
@for( int i = 0; i < result.Ks.Count(); i++ ) {
subm = (
...
).FirstOrDefault();
try {
sig = (
...
).Single();
} catch {
sig = null;
}
if( subm != null && subm.K != 0 ) {
<tr>
<td>@Html.CheckBoxFor(m => m.SelectedResults[kv.Key][isr][i])</td>
<td>@result.Ks[ i ]</td>
<td>@subm.i</td>
<td>@subm.v</td>
<td>@subm.g</td>
<td>@subm.d</td>
@if( sig != null ) {
<td>@sig.S</td>
<td>@sig.T</td>
} else {
<td>N/A</td>
<td>N/A</td>
}
</tr>
}
}
</table>
}
}
<button type="submit">Search</button>
}