Search code examples
asp.net-mvcasp.net-mvc-2asp.net-mvc-2-validation

ASP.NET MVC 2 "value" in IsValid override in DataAnnotation attribute passed is null, when incorrect date is submitted


This is my first question here on stack overflow. i need help on a problem i encountered during an ASP.NET MVC2 project i am currently working on. I should note that I'm relatively new to MVC design, so pls bear my ignorance. Here goes : I have a regular form on which various details about a person are shown. One of them is "Date of Birth". My view is like this

<div class="form-items">
            <%: Html.Label("DateOfBirth", "Date of Birth:") %>
            <%: Html.EditorFor(m => m.DateOfBirth) %>
            <%: Html.ValidationMessageFor(m => m.DateOfBirth) %>
</div>

I'm using an editor template i found, to show only the date correctly :

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.DateTime?>"%>
<%= Html.TextBox("", (Model.HasValue ? Model.Value.ToShortDateString() : string.Empty))%>

I used LinqToSql designer to create my model from an sql database. In order to do some validation i made a partial class Person to extend the one created by the designer (under the same namespace) :

[MetadataType(typeof(IPerson))]
public partial class Person : IPerson
{        //To create buddy class    }

public interface IPerson
{
    [Required(ErrorMessage="Please enter a name")]
    string Name { get; set; }
    [Required(ErrorMessage="Please enter a surname")]
    string Surname { get; set; }
    [Birthday]
    DateTime? DateOfBirth { get; set; }
    [Email(ErrorMessage="Please enter a valid email")]
    string Email { get; set; }
}

I want to make sure that a correct date is entered. So i created a custom DataAnnotation attribute in order to validate the date :

public class BirthdayAttribute : ValidationAttribute
{
    private const string _errorMessage = "Please enter a valid date";

    public BirthdayAttribute() : base(_errorMessage) { }

    public override bool  IsValid(object value)
    {
        if (value == null)
        {
            return true;
        }
        DateTime temp;
        bool result = DateTime.TryParse(value.ToString(), out temp);
        return result;
    }
}

Well, my problem is this. Once i enter an incorrect date in the DateOfBirth field then no custom message is displayed even if use the attribute like [Birthday(ErrorMessage=".....")]. The message displayed is the one returned from the db ie "The value '32/4/1967' is not valid for DateOfBirth.". I tried to enter some break points around the code, and found out that the "value" in attribute is always null when the date is incorrect, but always gets a value if the date is in correct format. The same ( value == null) is passed also in the code generated by the designer.

This thing is driving me nuts. Please can anyone help me deal with this? Also if someone can tell me where exactly is the point of entry from the view to the database. Is it related to the model binder? because i wanted to check exactly what value is passed once i press the "submit" button. Thank you.


Solution

  • Generally speaking all validation stuff is work after binder binded values. As you can understand it's not possible to bind dateTime value from string like "asdvfvk". So, when binder encounters with such an error it adds it to the ModelState (take a look at ModelState["DateOfBirth"].Errors[0].ErrorMessage), and binds default value. Default value for DateTime? is null, so that's why you always get it in IsValid method. It's normal. So as you can see validation for date has sence if you whant to check for example if it's bigger then some other date. If input string is incorrect no further verification have sence.

    What can you do?

    First straightforward way - you can correct your action like this

     [HttpPost]
            public ActionResult About(Person person, string dateOfBirth) {
                var birthdayAttribute = new BirthdayAttribute();
                if( !birthdayAttribute.IsValid(dateOfBirth)) {
                    ModelState["DateOfBirth"].Errors.Clear();
                    ModelState.AddModelError("DateOfBirth", birthdayAttribute.ErrorMessage);
                }
               .......
            }
    

    As you can see there is string dateOfBirth, so binder have no problems with binding string value. But this will not make your users happy.

    The better way - ensure that string will be in correct format with client Javascript. Actualy people use date picker controls for dates and feel themselves good.

    In addition take a look here http://forums.asp.net/t/1512140.aspx Especialy Brad Wilson's answer.