Tagged: dataannotations Toggle Comment Threads | Keyboard Shortcuts

  • danielsaidi 10:02 am on March 23, 2011 Permalink | Reply
    Tags: dataannotations, ssn, validation, validation attribute   

    Custom validation attributes 

    After some discussions with my great colleagues, I felt that it could be a good thing to write a short blog post about custom validation attributes. Maybe you have not used it yet.

    In short, validation attributes…are great.¬†For instance, consider these four validation scenarios:

    • E-mail addresses
    • Postal codes
    • Social security numbers
    • URLs

    All these validations could (to some extent) be expressed as regular expressions. Let’s start off by looking at four implementations (Swedish context):

    public class EmailAddressAttribute : RegularExpressionAttribute
    {
       public EmailAddressAttribute()
          : base(@"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$") { }
    
       public override bool IsValid(object value)
       {
          if (value == null || value.ToString().IsNullOrEmpty())
             return false;
          return base.IsValid(value);
       }
    }
    
    public class SwedishPostalCodeAttribute : RegularExpressionAttribute
    {
       public SwedishPostalCodeAttribute(bool optionalSpace = false)
          : base(optionalSpace ? "^\\d{3}\\ ?\\d{2}$" : "^\\d{5}$") { }
    
       public override bool IsValid(object value)
       {
          if (value == null || value.ToString().IsNullOrEmpty())
             return false;
          return base.IsValid(value);
       }
    }
    
    public class SwedishSsnAttribute : RegularExpressionAttribute
    {
       public SwedishSsnAttribute()
          : base("^\\d{6}-?\\d{4}$") { }
    
       public override bool IsValid(object value)
       {
          if (value == null || value.ToString().IsNullOrEmpty())
             return false;
    
          //Remove possible dash
          var noDash = value.ToString().Replace("-", "");
    
          //Verify the Luhn algorithm
          var sum = 0;
          for (var i = 0; i < 9; i++)
          {
             var tmpInt = int.Parse(noDash[i].ToString());
             tmpInt = tmpInt * (((i + 1) % 2) + 1);
             sum += (tmpInt > 9) ? tmpInt - 9 : tmpInt;
             sum = (sum > 10) ? sum - 10 : sum;
          }
    
          //Verify the check digit
          return (10 - sum) == int.Parse(noDash[9].ToString());
       }
    }
    
    public class UrlAttribute : RegularExpressionAttribute
    {
       public UrlAttribute()
          : base(@"^(http|ftp|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&:/~\+#]*[\w\-\@?^=%&/~\+#])?") { }
    
       public override bool IsValid(object value)
       {
          if (value == null || value.ToString().IsNullOrEmpty())
             return false;
          return base.IsValid(value);
       }
    }
    

    As you can see, the attributes for e-mail, postal codes and urls only use the regex (and a null/empty condition), while the social security attribute does a bit more.

    The Swedish postal code attribute, however, is insufficient. It should also verify (with some external service perhaps?) that the postal code actually exists, but hey…that requires some out of scope integrations. ūüôā

    This approach gathers the regex and the actual validation in one place. If one would need to use the regex separately, it can be accessed with the Pattern property. You can then use the regex to provide a shallow validation client-side, while using the attribute to perform a deeper validation server-side.

     
  • danielsaidi 11:37 am on July 5, 2010 Permalink | Reply
    Tags: , constraintexception, dataannotations, , ,   

    ConstraintException thrown when posting empty data for non-nullable properties 

    I am currently working with model validation, using an EF4 entity model, DataAnnotations and partial classes with MetadataType connections.

    In my model, I have an Employee entity for which some of properties are non-nullable. I have also created a partial class and a meta data class for model validation, as is described in this blog post.

    This works great. The Employee class is validated properly, with minimum effort. My entities are validated with standard validation attributes as well as custom ones. Lovely.

    However, the application crashes when I post empty text input elements in my Create/Edit views. A ConstraintException is thrown before my view controller actions are executed, which means that I cannot act on the constraint exception within my action.

    The exception is caused by the fact that empty posted data will cause the corresponding model properties to be set to null, which conflicts with the non-nullable properties in the entity model.

    However, since I have custom model validation classes in which I add¬†Required attributes to mandatory properties, I do not need the non-nullable attributes in my entity model. As such, I set the nullable property to (None)…and the ConstraintException is history!

     
    • Thomas 3:29 pm on September 24, 2010 Permalink | Reply

      Surely this is the incorrect approach, as it means you have no constraints set at the root database level. Which seems wrong.

      There is actually no problem with a constraint exception being thrown, that’s exactly what is supposed to happen.

      • danielsaidi 11:57 am on October 4, 2010 Permalink | Reply

        Hi Thomas! Thank you for your input, you are absolutely right. I have updated the post according to your feedback.

    • temStetLody 10:05 pm on June 21, 2011 Permalink | Reply

      Hello all! I like this forum, i found numberless interesting people on this forum.!!!

      Large Community, good all!

    • Hector 4:47 pm on June 3, 2012 Permalink | Reply

      I know, I know, it has been almost two years since this was posted.
      But just in case someone else is looking here for a solution, I just managed to get around.
      You may use the “DisplayFormat” DataAnnotation in order to override the default behavior (empty posted data causes corresponding model properties set to null), like this:

      [DisplayFormat(ConvertEmptyStringToNull = false)]

      Add the annotation for each non-nulleable attribute of the model. Then, EntityException is gone, and validation still works.

      Best regards.

      • danielsaidi 4:53 pm on June 3, 2012 Permalink | Reply

        Yeah, the post is rather old…and invalid. I should update it, but…well ūüėČ

        Thank you so much for your comment!

  • danielsaidi 1:03 am on July 5, 2010 Permalink | Reply
    Tags: callwithmodelvalidation, dataannotations, , , modelstate, ,   

    DataAnnotations and MetadataType fails in unit tests 

    This post describes how to solve the problem that model validation will not work for ASP.NET MVC2 (.NET 4.0) when testing a model that uses DataAnnotations and MetadataType to describe for its validation.

    First of all, ModelState.IsValid is always true, since the functionality that sets it to false if the model is invalid is never executed during the test cycle. This will cause your controllers to behave incorrectly during your tests.

    Second, MetadataType binding is ignored during the test cycle as well. This will cause the validation within it to be ignored as well, which in turn will cause the model to be valid although an object is invalid.

    My situation

    I am currently writing tests for a Create method in one of my controllers. I use NUnit as test framework. I have an EF4 Entity Model, in which I have a couple of entities. For instance, I have an Employee entity with FirstName, LastName and Ssn properties.

    To enable model validation, I create a partial Employee class in the same namespace as the EF4 entity model, then create a MetadataType class, which handles validation for the class. This approach is fully described in this blog post.

    In my EmployeeController, I have a Create method that takes an employee and tries to save it. If ModelState.IsValid is false, the controller returns the Create view again and displays the errors. If the model is valid, however, I create the employee and return the employee list.

    Easy enough. Well, when I started to write tests, I realized that ModelState.IsValid is always true, even if I provide the method with an invalid employee. Turns out that model validation is not triggered by the unit test.

    Trigger model validation within a test

    This blog post describes the ModelState.IsValid problem and provides a slick solution – the CallWithModelValidation Controller extension method.

    I added this extension method to my MVC2 project and used it instead of calling Create, as such:

       Before:
       var result = controller.Create(new Employee());
    
       After:
       var result = controller.CallWithModelValidation(c => c.Create(new Employee()), new Employee());

    And sure enough, this causes the test to trigger model validation. The only problem is that the model validation does not catch any errors within the model, even if the model is invalid.

    After some fiddling, I noticed that this error only occurs for partial objects that uses MetadataType to specify model validation. A class that describes its validation attributes directly is validated correctly.

    Turns out that the MetadataType class is ignored within test context. Thus, the model is always considered to be valid.

    Register MetadataType connections before testing

    This blog post describes the MetadataType problem and provides a slick solution – the InstallForThisAssembly method.

    This method must be placed within the same assembly as the model, in other words not the test project. I placed it in a ControllerExtensions class file and call it at the beginning of CallWithModelValidation. This works, but will not work if you move the extension to another project.

    Run it before your tests, and everything will work “as it should”.

    Hope this helps.

     
c
Compose new post
j
Next post/Next comment
k
Previous post/Previous comment
r
Reply
e
Edit
o
Show/Hide comments
t
Go to top
l
Go to login
h
Show/Hide help
shift + esc
Cancel