I am trying to get ASP.NET MVC 3 to create forms from complex, nested objects. I found one validation behavior that was unexpected, and I'm not sure if this is an error in DefaultModelBinder or not.
If I have two objects, the parent is called OuterObject and has a property of type InnerObject (child):
public class OuterObject : IValidatableObject { [Required] public string OuterObjectName { get; set; } public InnerObject FirstInnerObject { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (!string.IsNullOrWhiteSpace(OuterObjectName) && string.Equals(OuterObjectName, "test", StringComparison.CurrentCultureIgnoreCase)) { yield return new ValidationResult("OuterObjectName must not be 'test'", new[] { "OuterObjectName" }); } } }
Here's InnerObject:
public class InnerObject : IValidatableObject { [Required] public string InnerObjectName { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (!string.IsNullOrWhiteSpace(InnerObjectName) && string.Equals(InnerObjectName, "test", StringComparison.CurrentCultureIgnoreCase)) { yield return new ValidationResult("InnerObjectName must not be 'test'", new[] { "InnerObjectName" }); } } }
You will notice that I put both on them. Only some dummy checks to say that a value cannot be equal to a โtestโ.
Here is the view to be displayed in (Index.cshtml):
@model MvcNestedObjectTest.Models.OuterObject @{ ViewBag.Title = "Home Page"; } @using (Html.BeginForm()) { <div> <fieldset> <legend>Using "For" Lambda</legend> <div class="editor-label"> @Html.LabelFor(m => m.OuterObjectName) </div> <div class="editor-field"> @Html.TextBoxFor(m => m.OuterObjectName) @Html.ValidationMessageFor(m => m.OuterObjectName) </div> <div class="editor-label"> @Html.LabelFor(m => m.FirstInnerObject.InnerObjectName) </div> <div class="editor-field"> @Html.TextBoxFor(m => m.FirstInnerObject.InnerObjectName) @Html.ValidationMessageFor(m => m.FirstInnerObject.InnerObjectName) </div> <p> <input type="submit" value="Test Submit" /> </p> </fieldset> </div> }
.. and finally this is the HomeController:
public class HomeController : Controller { public ActionResult Index() { var model = new OuterObject(); model.FirstInnerObject = new InnerObject(); return View(model); } [HttpPost] public ActionResult Index(OuterObject model) { if (ModelState.IsValid) { return RedirectToAction("Index"); } return View(model); } }
What you will find is that when the model receives the default confirmation of ModelBinder, the Validate method in InnerObject gets twice, but the Validate method does not get in OuterObject at all.
If you delete the IValidatableObject file from "InnerObject", one of the "OuterObject" will be deleted.
Is this a mistake, or should I expect it to work that way? Should I expect this to be the best workaround?