I updated my web sites to
ASP.NET MVC 3 RC2, and started receiving exceptions when model binding. If you want the short version, visit this
forum post for the answer. While my errors weren't caused by the same issue that was originally reported in that thread, the fix does work. To sum up even further, just add this line of code in your Global.asax Application_Start method:
ModelMetadataProviders.Current = new DataAnnotationsModelMetadataProvider();
The longer story is that I have a pretty involved multi-step (think "wizard"), deep-level object graph built up over many pages, so I immediately feared that I was unknowingly exploiting some loophole that just got fixed for RC2. I built a test case, so in the interest of not having to build that test case up from scratch again, it looked like this:
Model
public class Foo
{
private List<Bar> _bars;
private List<Baz> _bazs;
public Foo()
{
_bars = new List<Bar>();
_bazs = new List<Baz>();
}
public List<Bar> Bars
{
get { return _bars; }
set { _bars = value; }
}
public List<Baz> Bazs
{
get { return _bazs; }
set { _bazs = value; }
}
}
public class Bar
{
public int Amount { get; set; }
}
public class Baz
{
public int Value { get; set; }
}
Controller
public ActionResult Index()
{
var foo = new Foo();
foo.Bars.Add(new Bar { Amount = 4});
foo.Bars.Add(new Bar { Amount = 23 });
foo.Bars.Add(new Bar { Amount = 30 });
foo.Bazs.Add(new Baz { Value = 100 });
foo.Bazs.Add(new Baz { Value = 200 });
TempData["foo"] = foo;
return View(foo);
}
[HttpPost]
public ActionResult Index(IList<Bar> bars, IList<Baz> bazs)
{
Foo foo = (Foo) TempData["foo"];
foo.Bars.Clear();
foo.Bars.AddRange(bars);
foo.Bazs.Clear();
foo.Bazs.AddRange(bazs);
TempData["foo"] = foo;
return RedirectToAction("Summary");
}
public ActionResult Summary()
{
Foo foo = (Foo)TempData["foo"];
return View(foo);
}
View
<% using(Html.BeginForm()) { %>
<%
int barid = 0;
foreach (var item in Model.Bars){%>
<%= Html.Hidden("Bars.index", barid) %>
Bar:<%= Html.TextBox("Bars[" + barid +"].Amount", item.Amount) %>
<% barid++;
}%>
<%
int bazid = 0;
foreach (var item in Model.Bazs)
{%>
<br />
<%=Html.Hidden("Bazs.index", bazid)%>
Baz: <%=Html.TextBox("Bazs[" + bazid + "].Value", item.Value)%>
<% bazid++;
}%>
<p />
<input type="submit" value="Next" />
<% } %>
I would receive the following call stack when trying to submit the form:
The parameters dictionary contains an invalid entry for parameter 'bazs' for method
'System.Web.Mvc.ActionResult Index(System.Collections.Generic.IList`1[MvcApplicationRC2.Models.Bar], System.Collections.Generic.IList`1[MvcApplicationRC2.Models.Baz])'
in 'MvcApplicationRC2.Controllers.HomeController'. The dictionary contains a value of type 'System.Collections.Generic.List`1[MvcApplicationRC2.Models.Bar]',
but the parameter requires a value of type 'System.Collections.Generic.IList`1[MvcApplicationRC2.Models.Baz]'.
Parameter name: parameters
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.ArgumentException: The parameters dictionary contains an invalid entry for parameter 'bazs' for method
'System.Web.Mvc.ActionResult Index(System.Collections.Generic.IList`1[MvcApplicationRC2.Models.Bar], System.Collections.Generic.IList`1[MvcApplicationRC2.Models.Baz])'
in 'MvcApplicationRC2.Controllers.HomeController'. The dictionary contains a value of type 'System.Collections.Generic.List`1[MvcApplicationRC2.Models.Bar]',
but the parameter requires a value of type 'System.Collections.Generic.IList`1[MvcApplicationRC2.Models.Baz]'.
Parameter name: parameters
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be
identified using the exception stack trace below.
Stack Trace:
[ArgumentException: The parameters dictionary contains an invalid entry for parameter 'bazs' for method 'System.Web.Mvc.ActionResult
Index(System.Collections.Generic.IList`1[MvcApplicationRC2.Models.Bar], System.Collections.Generic.IList`1[MvcApplicationRC2.Models.Baz])' in
'MvcApplicationRC2.Controllers.HomeController'. The dictionary contains a value of type 'System.Collections.Generic.List`1[MvcApplicationRC2.Models.Bar]',
but the parameter requires a value of type 'System.Collections.Generic.IList`1[MvcApplicationRC2.Models.Baz]'.
Parameter name: parameters]
System.Web.Mvc.ActionDescriptor.ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary`2 parameters, MethodInfo methodInfo) +484514
System.Web.Mvc.<>c__DisplayClass1.<Execute>b__0(ParameterInfo parameterInfo) +18
System.Linq.WhereSelectArrayIterator`2.MoveNext() +85
System.Linq.Buffer`1..ctor(IEnumerable`1 source) +325
System.Linq.Enumerable.ToArray(IEnumerable`1 source) +78
System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters) +133
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +27
System.Web.Mvc.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12() +55
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation) +263
System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +19
System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor, IDictionary`2 parameters) +191
System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +343
System.Web.Mvc.Controller.ExecuteCore() +116
System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +97
System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +10
System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +37
System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +21
System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +12
System.Web.Mvc.Async.WrappedAsyncResult`1.End() +62
System.Web.Mvc.<>c__DisplayClasse.<EndProcessRequest>b__d() +50
System.Web.Mvc.SecurityUtil.<GetCallInAppTrustThunk>b__0(Action f) +7
System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(Action action) +22
System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +60
System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +9
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8841105
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184