RIA Services offers powerful features for flowing validation from your entity model all the way up to the Silverlight UI.  In fact, flowing your validation rules from the server into your form controls was one of the primary tenets of RIA Services V1, and our team did the work of getting validation integrated deeply into Silverlight, long before RIA Services was released.  In this post we’ll take a look at the standard validators that are offered.

System.ComponentModel.DataAnnotations.ValidationAttribute

RIA Services employs the ValidationAttribute class from System.ComponentModel.DataAnnotations for defining validation rules on your entity model.  ValidationAttribute itself is abstract (VB: MustInherit) but any class that derives from ValidationAttribute will be treated as a “validator.”  Validators can be applied to properties and classes alike, allowing for property-level or object-level validators.  Some common property-level validators are predefined within the DataAnnotations assembly:

  1. RequiredAttribute
  2. RangeAttribute
  3. StringLengthAttribute
  4. RegularExpressionAttribute

These four validators alone will cover a great deal of your validation scenarios.  Of course, we all know that when you solve a problem using a regular expression, you now have two problems.  But in all seriousness, these four little attributes can be extremely useful and they are what we’ll be focusing on within this post.  Here’s a sample class that utilizes these validators and a screen shot for what the UI does with these validators.

 
using System;
using System.ComponentModel.DataAnnotations;
using RudeValidation.Web.Resources;
using RudeValidation.Web.Validators;
 
namespace RudeValidation.Web.Models
{
    public partial class Meeting
    {
        [Key]
        public int MeetingId { get; set; }
 
        [Required]
        public DateTime Start { get; set; }
 
        [Required]
        public DateTime End { get; set; }
 
        [Required]
        [StringLength(80, MinimumLength = 5,
            ErrorMessageResourceType = typeof(ValidationErrorResources),
            ErrorMessageResourceName = "TitleStringLengthErrorMessage")]
            // {0} must be at least {2} characters and no more than {1}.
        public string Title { get; set; }
 
        public string Details { get; set; }
 
        [Required]
        [RegularExpression(@"\d{1,3}/\d{4}",
            ErrorMessage = "{0} must be in the format of 'Building/Room'")]
        public string Location { get; set; }
 
        [Range(2, 100)]
        [Display(Name = "Minimum Attendees")]
        public int MinimumAttendees { get; set; }
 
        [Range(2, 100)]
        [Display(Name = "Maximum Attendees")]
        public int MaximumAttendees { get; set; }
    }
}

 

Validation errors display

Error Messages

All validators have default error messages (including ValidationAttribute itself, so that all derived validators get some sort of message).  When using validators, you can easily override the error message using the ErrorMessage property.  This allows for context-appropriate error messages to be used on your model.  The Location property above shows an example of setting the ErrorMessage.  Notice that the error message uses a {0} placeholder—this is completely optional, but it allows for the display name of the property to be plugged in wherever the placeholder is used.

It’s important to note that the above sentence said, “the display name of the property,” and not, “the property name.”  This is because the DisplayAttribute is respected when error messages are being formatted.  If the property has a [Display] attribute declared, and the Name property has been specified on it, then that value will be used in place of the actual property name.  When a Display Name is not available, the property name itself will be used.  Whenever you have property names that are compound words, you should use a [Display] attribute to ensure your validation error messages are user-friendly.

RESX file Access Modifier: PublicAnd speaking of being user-friendly, let’s talk about localization for a moment.  If you are making your application localizable, you can utilize the ErrorMessageResourceType and ErrorMessageResourceName properties on the validators to specify localizable error messages.  Be sure that your resource file has the access modifier set to public though, or else RIA Services won’t be able to propagate your validator because it will fail to load the type you specify as the ErrorMessageResourceType.  The resources need to be available within the Silverlight project too.  Fortunately the Business Application Project template that ships with RIA Services sets all of this up for you, and you can simply add your error messages to the ValidationErrorResources.resx file and everything will work straight-away.

Some validators offer additional placeholders for their error messages beyond the {0} placeholder for the display name of the property.

  1. [Required] – No additional placeholders
  2. [Range(minimumValue, maximumValue)] – {1} = minimumValue; {2} = maximumValue
  3. [StringLength(maxLength)] – {1} = maxLength
  4. [StringLength(maxLength, MinimumLength = minLength)] – {1} = maxLength; {2} = minLength
    Note – Yes, {2} is the minimum length so that {1} is always the maximum length.
  5. [RegularExpression(regEx)] – {1} = the regular expression specified, although it would be a crime to allow this to display in your UI (even though it will by default)

Nothing is Required, Except for Required

When using RangeAttribute, StringLengthAttribute, or RegularExpressionAttribute, you might notice something that seems bizarre at first—these validators all indicate success when the field is blank.  You might ask, “If I’ve said my value must be between 2 and 100, why don’t I get an error message if the field is blank?”  The explanation for this is actually quite simple: Unless the field is marked as [Required], it is not required.  Furthermore, if [Range], [StringLength], and [RegularExpression] enforced non-blank values, then you’d have no way of validating optional field values.  Therefore it’s essential that the [Required] validator is the only one that ever indicates failure if the value is blank.

RIA Services Validation Recap:

  1. Standard Validators
  2. Custom Validation Methods
  3. Custom Reusable Validators
  4. Attribute Propagation
  5. Validation Triggers
  6. Cross-Field Validation
  7. Entity-Level Validation
  8. Providing ValidationContext
  9. Using ValidationContext (Cross-Entity Validation)
  10. ViewModel Validation with Entity Rules

[9/6/2011] The source code for everything shown during the series is available on GitHub.

Digging Deeper

I will be digging deeper into RIA Services Validation in upcoming blog posts, but I wanted to make sure the foundation was laid for how these standard validators work.  In future posts, we’ll be exploring how RIA Services actually propagates your validators to the client, how you can create custom validators by using [CustomValidation] and by deriving from ValidationAttribute directly, as well as when/how RIA Services will invoke each kind of validator.