views:

137

answers:

2

Hi all,

I've used stackoverflow many a time to solve my problems (code related!) but this is the first time I've needed to post a question cause I cannot work out whats wrong.

When I enable client side validation on a view that allows editing of a collection of objects which use DataAnnotations for validation the following exception is raised:

[KeyNotFoundException: The given key was not present in the dictionary.]
System.Collections.Generic.SortedDictionary`2.get_Item(TKey key) +6129977
System.Web.Mvc.Html.ValidationExtensions.ValidationMessageHelper(HtmlHelper htmlHelper, ModelMetadata modelMetadata, String expression, String validationMessage, IDictionary`2 htmlAttributes) +840
System.Web.Mvc.Html.ValidationExtensions.ValidationMessageFor(HtmlHelper`1 htmlHelper, Expression`1 expression, String validationMessage, IDictionary`2 htmlAttributes) +138
System.Web.Mvc.Html.ValidationExtensions.ValidationMessageFor(HtmlHelper`1 htmlHelper, Expression`1 expression) +106
ASP.views_test_test_aspx.__Render__control1(HtmlTextWriter __w, Control parameterContainer) in d:\SVN\Discover2 - trunk\Discover2.Web\Views\Test\Test.aspx:18
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +109
System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +8
System.Web.UI.Page.Render(HtmlTextWriter writer) +29
System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer) +56
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +100
System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3060

Line 18:    <%: Html.ValidationMessageFor(m => Model[i].Name)%>

If I remove the <% Html.EnableClientValidation(); %> call no exception is raised and server side validation works as expected.

Here is my test Model:

public class Dog {
  [Required]
  public string Name { get; set; }
}

and view:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<IList<Discover2.Web.Controllers.Dog>>" %>
<% Html.EnableClientValidation(); %>
<%using (Html.BeginForm()) { %>
<%for (int i = 0; i < Model.Count(); i++) {%>
    <div>
        <%: Html.LabelFor(m => Model[i].Name) %>
        <%: Html.TextBoxFor(m => Model[i].Name)%>
        <%: Html.ValidationMessageFor(m => Model[i].Name)%>
    </div>
 <%} %>
 <button type=submit>Save</button>

<%} %>

Any ideas on how to get this working would be appreciated as its been doing my head in trying to get this to work!!!

Thanks

A: 

I think the problem maybe with the names... i mean since you are using -

<%: Html.TextBoxFor(m => Model[i].Name)%>
 <%: Html.ValidationMessageFor(m => Model[i].Name)%>

i cant test out right now but this might work-

<%: Html.Label("Name",Model[i].Name) %>
<%: Html.TextBox("Name", Model[i].Name)%>
<%: Html.ValidationMessage("Name",Model[i].Name)%>
Misnomer
Thanks for that but no joy there. I tried changing the Name of the property to DogsName after your suggestion re the names but this didn't work either. Then tried Html.ValidationMessage(String.Format("Model[{0}].DogsName", i.ToString())) which didn't raise the error but no validation messages were shown so then tried removing Model Html.ValidationMessage(String.Format("[{0}].DogsName", i.ToString())) and the original exception is raised again! I guess this is showing us what MVC is doing but doesn't help me come up with an answer!! Any more ideas??
Andy
A: 

Finally got to the bottom of this one. Basically the MVC ValidationMessageHelper function was failing to generate an id for the span element generated by the validation helper. The CreateSanitizedId method on the TagBuilder class was returning null because the modelName was "[0].Name" and Id's cannot begin with anything other than a letter. The KeyNotFoundException was then being thrown as the TagBuilder did not contain an id.

Anyway the way around the problem was to implement a simple View Model instead of setting the Model to be a collection directly so that Dogs was a property on the model and therefore the modelName used to generate the id becomes "Dogs[0].Name" which works.

Andy