tags:

views:

134

answers:

1

I am using the Data Annotation Validator, outlined here:

http://www.asp.net/learn/mvc/tutorial-39-cs.aspx

The Data Annotations Model Binder is on codeplex:

http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=24471

I've downloaded the code, built it, and referenced the new System.ComponentModel.DataAnnotations.dll. I've also set the default model binder to use Microsoft.Web.Mvc.DataAnnotations.dll. It works fine when binding to a simple object (e.g, Order), but if I bind to an Order that has a CreditCard object, I get an error in the new binder:

Object reference not set to an instance of an object, DataAnnotationsModelBinder.cs Line: 60.

In my example, the fullPropertyKey is "Card", and modelState is null, so its obviously having an issue with the Card property of Order.

ModelState modelState = bindingContext.ModelState[fullPropertyKey];

// Only validate and bind if the property itself has no errors
if (modelState.Errors.Count == 0) {
    if (OnPropertyValidating(controllerContext, bindingContext, propertyDescriptor, newPropertyValue)) {
           SetProperty(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
           OnPropertyValidated(controllerContext, bindingContext, propertyDescriptor, newPropertyValue);
    }
}

Does the data annotaions binder support this? The stock binder has no issue with the Order -> CreditCard structure (minus the validation, of course). Test code:

Controller & Model:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.ComponentModel.DataAnnotations;

namespace PAW.Controllers
{
    //MODEL
    public class Order
    {
        [Required]
        [DataType(DataType.Text)]
        [StringLength(5)]
        public string CustomerName { get; set; }

        public CreditCard Card { get; set; }

        public Order()
        {
            this.Card = new CreditCard();
        }
    }
    public class CreditCard
    {
        [StringLength(16), Required]
        public string Number { get; set; }
    }

    //CONTROLLER
    public class TestController : Controller
    {
        [AcceptVerbs(HttpVerbs.Get)]
        public ActionResult Index()
        {
            return View(new Order());
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Index(Order o)
        {
            if (ModelState.IsValid)
            {
                //update
            }
            return View(o);
        }
    }
}

The View:

<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<PAW.Controllers.Order>" %>

<html xmlns="http://www.w3.org/1999/xhtml" >
<body>        
    <% using(Html.BeginForm()) { %>

        <%=Html.ValidationSummary("Errors: ") %>

        <div>
            Name (max 5 chars): <%=Html.TextBox("CustomerName", Model.CustomerName)%>
        </div>

        <div>
            CC#: <%=Html.TextBox("Card.Number", Model.Card.Number)%>
        </div>

        <input type="submit" value="submit" />
    <% } %>
</body>
</html>
A: 

This is infact a bug in the DataAnnotationModelBinder. It's suppose to be fixed in the upcoming version though.

In this similar question on SO i posted my quick fix in the DataAnnotationModelBinder to make it work and a quick example of complex/deep binding.

Martijn Laarman
thanks. I implemented the fix. I was looking at that thinking it has to be something bigger than just a null check.. guess I was wrong. :-)
ericvg