Ranting and raving about anything I feel like complaining about.

More Validation Fun - But Not Done Yet

Url: http://silverlight.net/blogs/jesseliberty/archi...

Silverlight Logo

I was reading Jesse Liberty's Validation post today and it reminded me of a discussion that I had with my students last week about doing validation. While performing the validation is interesting, for them they needed to worry about localization. In the face of this I suggested the new validation attributes. These attributes live in the System.ComponentModel.DataAnnotations namespace.  These attributes include:

  • Required
  • Range
  • RegularExpression
  • StringLength
  • CustomValidation

Each of these can be applied to properties like so:

public class Game
{
  [Required]
  [StringLength(255)]
  public string ProductName { get; set; }
  
  [Required]
  [Range(0m,250m)]
  public decimal ListPrice { get; set; }
     
  [CustomValidation(typeof(MyValidator), "CheckReleaseDate")]
  public DateTime ReleaseDate { get; set; }
}

 

In Jesse's example, instead of using attributes he used exceptions to notify the validation UI in Silverlight 3. This works fine but my students were concerned about localizing the string in the exception. While they could have done it with a string table, I suggested that what they might want instead were the validation attributes.  In each attribute you can specify the error message like so:

[Required(ErrorMessage="ProductName is required.")]
public string ProductName { get; set; }

 

But to localize it the trick is to actually specify the name message in the attribute like so:

[Required(ErrorMessageResourceName="PRODUCTNAMEREQUIRED")]
public string ProductName { get; set; }

 

But after asserting that this was a good solution, I didn't have a good example of how to force validation with these attributes.  Of course, in Silverlight 3 you could use the DataForm to do this but I wanted to be able to point my students at a way of using it anywhere.  (The DataForm is great, I just like to have options.)

After a couple of hours with Reflector, I had a workable solution:

public class Game
{
  string _name;
  decimal _listPrice;
  DateTime _releaseDate;

  [Required(ErrorMessageResourceName="PRODUCTNAMEREQUIRED")]
  public string ProductName 
  {
    get { return _name; }
    set
    {
      if (value != _name)
      {
        _name = value;
        Validate();
      }
    }
  }
  
  [Required]
  [Range(0m,250m)]
  public decimal ListPrice 
  {
    get { return _listPrice; }
    set
    {
      if (value != _listPrice)
      {
        _listPrice = value;
        Validate();
      }
    }
  }
     
  [CustomValidation(typeof(MyValidator), "CheckReleaseDate")]
  public DateTime ReleaseDate 
  {
    get { return _releaseDate; }
    set
    {
      if (value != _releaseDate)
      {
        _releaseDate = value;
        Validate();
      }
    }
  }

  void Validate()
  {
    var ctx = new ValidationContext(this, null, null);
    Validator.ValidateObject(this, ctx);
  }
}

 

The trick was to create a validation routine directly in the data class that I was binding to. The Validate method creates a ValidationContext object (which contains the metadata about the attributes) and use it to call ValidateObject on the Validator class. This reads the attributes and builds the data (as an exception) that the validation UI requires. The trick is to them call the Validate method in the setters of the object.

The problem I have is that this doesn't quite play well with Web Service or Data Service data (it would require that we modify the generated code). I am still looking for a better solution (like the MetaDataType solution that Dynamic Data uses).  When I find the right person at MS who can clue me in, i'll blog about it here.

 
 

Comments

Gravatar Any idea on how to invoke a validation method like that from the client side? Essentially what I want is when my entity is newly constructed client side, for it to automatically run all the validation rules. The problem is adding [Shared] to the Validate method makes no difference so I can't invoke it client side. I added a call to Validate() in the ctor, but that only fires server side.
Gravatar I was wondering if you have also found a solution for localizing the binding-validation exceptions thrown by the system. For instance when an integer-field is empty, the system throws the exception: 'Input is not in a correct format'.

Add a Comment

*
*
*