I am using MVC 4, .Net 4 and Visual Studio 2012.
I am trying to use a rather complex model with one of my views, and I am having serious problems with its proper binding.
The model wraps a dictionary with integer keys and values, which are lists of bools lists.
Basically, the search was performed on the elements indicated by an integer, each element had several search conditions, and for each of these terms we have a list of results. I show the results on the page and check the box next to each result. For each result, the user will indicate whether they want some things done in the next step by checking the box.
At the moment, the checkboxes are displayed correctly, including predefined values โโfrom the controller, but when I click 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 seems to me that I have something to do with using the Dictionary, which, as I was told, does not work well as a model. I may have to switch to something else, but I would prefer if I did not have to. It looks like there might be an answer: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx or List of checkboxes for a complex type in asp.net mvc or How to bind a type parameter a dictionary for both GET and POST actions on ASP.NET MVC , but I found them after the whole question was written, and I still didn't understand, so maybe someone 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 is the model:
public class AutoResolveModel { public Dictionary<int, List<List<bool>>> SelectedResults { get; set; } public AutoResolveModel() { SelectedResults = new Dictionary<int, List<List<bool>>>(); } }
Since this may make a difference, here is the ViewBag.iidToData structure that contains the results that will 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 are some important parts from View with variable names that have been 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> }