My goal with this blog post series is to provide an end-to-end tour of how RIA Services validation works and how you can leverage its capabilities to build rich, responsive applications.  A key factor for making the most of validation is fully understanding when and how RIA Services invokes your validation rules.  Awareness of the various validation triggers will help you implement your validators so they are fired as you expect, when you expect.  In this post will dig into the RIA Services codebase and identify every place where entity validation is invoked.

Client-Side Validation

Property Setters – Events Raised

this.ValidatePropertyAs we saw in the Attribute Propagation article, RIA Services injects validation logic within your entity property setters.  This comes in the form of a call to this.ValidateProperty(string propertyName, object value).  The ValidateProperty method is defined within the Entity base class that RIA Services uses for all of the generated entities.  This method has quite a bit of logic in it—let’s examine the flow:

  1. If the entity is currently being deserialized or merged (from a load or submit operation), then validation is bypassed entirely;
  2. If the entity is read-only, an InvalidOperationException is thrown with the following message: “This entity is currently read-only. One of the following conditions exists: a domain method has been invoked, a submit operation is in progress, or edit operations are not supported for the entity Type.”
  3. If the specified property has an [Editable] attribute applied to it that indicates that the property cannot be edited, then an InvalidOperationException is thrown with the following message: “The {0} property is read only.”
  4. If the specified property has any attributes that derive from ValidationAttribute, then validation proceeds.

When validation proceeds, the ValidateProperty(string propertyName, object value) method creates a ValidationContext with the MemberName specified as the property name being validated.  Then, another overload of ValidateProperty is called: ValidateProperty(ValidationContext validationContext, object value).  This overload of ValidateProperty is virtual, which means you can override it in your Entity classes if you wish to change the core behavior of property setter validation.

The default implementation of ValidateProperty(ValidationContext validationContext, object value) uses the Validator static class within System.ComponentModel.DataAnnotations, calling its TryValidateProperty method to accumulate validation errors for the property and the intended value.  The collected errors are then applied to the Entity’s ValidationErrors collection, which triggers ErrorsChanged events for the INotifyDataErrorInfo interface, as appropriate.  This informs the UI that errors have occurred, and the error messages are displayed automatically.

Entity State During Property Validation

There’s one very subtle detail that is important to point out related to the state of your entity when ValidateProperty is invoked.  If you look closely at the generated property setter code, you’ll see that the private field for this._title is not updated with the new value until after ValidateProperty has completed.  This might seem odd at first, but with a quick look at the matrix below, you’ll see why this decision was made.

Validator Requirement

Update Value Before Validation

Update Value After Validation

Access New Value Use ‘value’ parameter or property getter Use ‘value’ parameter
Access Old Value Unavailable Use property getter
Prevent Property Update Unavailable Throw ValidationException
Entity State After Property Validation

Another subtle detail about property validation lies in the state of the entity after property validation occurs.  In the default implementation of ValidateProperty, events are raised when validation errors occur, but no exceptions are thrown.  This means the backing field for the property is updated immediately after validation is performed, and even when validation resulted in errors, the property value is updated to to new value.  Therefore, it is commonplace for your entities to have invalid property values.  Note: This wasn’t always the case.  Before Silverlight 4 introduced INotifyDataErrorInfo, RIA Services would throw a ValidationException during validation, so in early preview releases of RIA Services, the behavior was different.

Ending Edit Sessions – Events Raised

The Entity base class implements an interface called IEditableObject.  This interface defines behavior for allowing an object to have edit sessions applied to it, with the following members: BeginEdit, CancelEdit, and EndEdit.  When BeginEdit is called, an edit session begins and the state of the entity is recorded.  If CancelEdit is called to close the edit session, the entity is reverted to its state at the time BeginEdit was invoked, and validation does not occur.  If the edit session is closed by calling EndEdit, then the entity will validate itself against its new state.

An edit session typically involves updating property values, which will trigger property setter validation as described above.  But when an edit session is ended, the entity is given an opportunity to perform what is called “entity-level validation.”  We have seen examples of validation attributes applied to entity properties, but it’s also possible to apply validation attributes to an entity class itself.  Many times, there are no simple property-edit triggers to perform cross-field validation, so this logic can be applied on the entity class itself, and whenever an edit session for the entity is ended, these validators will be invoked.  We will explore entity-level validators more in our next post!

During EndEdit, entity-level validators aren’t the only ones invoked though.  In fact, the following algorithm is used:

  1. Execute all [Required] validators on the entity’s properties.  If any required properties are missing values, validation fails and stops (but multiple required-field validation errors can occur).
  2. Execute the remainder of the property-level validators.  If any property is found to be invalid, validation fails and stops (but multiple property validation errors can occur).
  3. Execute all entity-level validators.  If any entity-level validator is found to be invalid, validation fails and stops (but multiple validation errors can occur).

Notice that validation stops if there are any required properties that haven’t been satisfied.  This is done so that all other validators can assume that any required value has been specified.  Similarly, if properties are found to be invalid, there’s no sense in executing entity-level validation—entity-level validators can assume that if they are being called, all properties are in a valid state.  If these blocks were not in place, you’d find yourself having to repeat property validation within your entity validation logic.

Once validation completes, the edit session will be committed and closed regardless of validation success.  This is largely because the IEditableObject interface is ignorant of validation and it has no concept for TryEndEdit.  Any validation errors that occur during EndEdit will be added to the entity’s ValidationErrors collection, and INotifyDataErrorInfo.ErrorsChanged events will be raised.  The UI then displays the validation errors to the end user.

Submitting Changes – SubmitOperation Error

After you have committed edits to your entities, the next step is to submit those changes to the server. This is done through the DomainContext.SubmitChanges method.  This method will perform validation against all entities that have pending changes, ensuring that none of them is in an invalid state before submitting the changeset to the server.  The method will perform entity-level validation much like EndEdit did, with 3-stage validation.  Any entities that are found to have validation errors will have their ValidationErrors collections populated.  Additionally, if there were any validation errors, the SubmitOperation will fail (without even going to the server), and the EntitiesInError collection will contain all of the entities that had validation errors.

Custom Update Methods – ValidationException Thrown

In addition to standard CRUD operations, RIA Services supports custom update methods, where business logic can be encapsulated within a DomainService method.  For example, a DomainService that exposes a Meeting entity would likely have Query, Insert, Update, and Delete methods for an Meeting, but it could also have a CancelMeeting method, which is a custom update method for the Meeting entity.  On the client, this method shows up as a CancelMeeting method on the Meeting entity itself, in addition to a CancelMeeting method on the DomainContext.  The DomainContext method will simply call the CancelMeeting method on the entity specified, so both approaches have the same result.

If you are using a custom update method, and you invoke the method on the client, the entity will be validated using a routine similar to what we saw with EndEdit, but with one notable difference.  With EndEdit, validation failures were added to the ValidationErrors collection, but the operation still succeeded.  When invoking a custom update method on an entity, a validation failure will result in a ValidationException being thrown.  This means that calls to these custom update methods should be wrapped in try/catch blocks that will catch any ValidationException that occurs, informing the end user that the operation could not be completed.

Server-Side Validation

While RIA Services performs client-side validation every step of the way toward submitting data to the server, that’s simply not enough.  No client/server application should ever trust data that came from the client; validation must always be repeated on the server to ensure the data submitted by the client is in valid form.  Fortunately, RIA Services does this for you seamlessly and automatically.

Submitting Changes – SubmitOperation Error

When a DomainContext.SubmitChanges call from the client passes client-side validation, a changeset is submitted to your DomainService.  Before your insert, update, and delete methods are called, RIA Services will validate all entities included in the changeset.  Once again, the same 3-stage validation will be respected.  If any entities in the changeset are invalid, the submission is canceled and the validation errors are returned to the client.  This will result in a SubmitOperation error, and the EntitiesInError collection will be populated, and for each entity that had validation failures, the ValidationErrors collection will also be populated.  Whether the validation failures occurred on the client or on the server, the result is the same, allowing a consistent experience for both you and the end user.

IValidatableObject

Another feature available on the server is an interface called IValidatableObject.  This interface was added in .NET 4.0 for RIA Services.  This interface has a single member, a Validate() method that returns an IEnumerable<ValidationResult>.  This interface allows your server-side entities to encapsulate additional entity-level validation logic outside the validation attributes.  IValidatableObject.Validate() is actually called as a 4th stage of validation on the server, and it is only called if the first 3 stages of validation were successful.  Therefore, the server-side algorithm for validation is actually as follows:

    1. Execute all [Required] validators on the entity’s properties.  If any required properties are missing values, validation fails and stops (but multiple required-field validation errors can occur).
    2. Execute the remainder of the property-level validators.  If any property is found to be invalid, validation fails and stops (but multiple property validation errors can occur).
    3. Execute all entity-level validators.  If any entity-level validator is found to be invalid, validation fails and stops (but multiple validation errors can occur).
    4. Check the entity for an IValidatableObject implementation; if implemented, execute the Validate() method.  If any validation errors are returned, validation fails.
Server-Only Validation

There are other benefits to having server-side validation occur during change submission beyond re-executing the same validators that existed on the client.  It’s quite common to have validation logic that can only be performed on the server.  Consider a scenario where validation involves querying a database—this cannot be done synchronously in Silverlight.  In this situation, a custom validation method or attribute can be applied to your entity, using a method or attribute that isn’t shared with the client.  These validators will be invoked on the server even though they were not propagated to the client.

Additional Validation Triggers

In addition to the validation that RIA Services invokes, the Silverlight SDK and Toolkit also have validation features integrated into UI controls.  I won’t be going into explicit detail on these controls, but it’s important to understand that these additional validation triggers exist and they result in your validation attributes being invoked repeatedly when using these UI controls.

DataGrid

In the Silverlight SDK, the DataGrid control is validation-aware, and it performs validation aggressively.  As you begin editing a row, validation will occur to get the initial validation state of the entity.  Then, as you edit cell values, the properties will be validated.  And lastly, when you attempt to commit a row, the entity will be validated once more.  You will notice that your validation attributes get invoked many times during this process, since the DataGrid cannot assume the entity is self-validating, and the entity cannot assume the UI is performing validation either.

DataForm

In the Silverlight Toolkit, the DataForm control is validation-aware, and it too performs validation pessimistically, although not quite as aggressively as DataGrid.  DataForm will ensure that all properties’ values have been validated against the currently-displayed form values when the end user attempts to commit an edit session.  This involves an iteration over all form fields, forcing the currently-displayed values to be passed to the property setters again.  Additionally, DataForm will perform full object validation before allowing the current item selection to change.

Note: The RIA Services ‘Business Application Project’ template includes a copy of the DataForm control as well.  This is shipped as a standalone assembly within the project template, but the assembly comes from the Silverlight Toolkit.

RIA Services Validation Recap

This post is part of a series dedicated to RIA Services Validation.  We have now learned about all of the code paths in RIA Services that execute entity validation.  You now know when to expect your validation logic to get invoked, and in what various states validation can occur.  I am hopeful that this knowledge will allow you to provide richer experiences for your users.

Here’s a look at the posts in this series:

    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

We have now exposed all of the details for how your entity validation logic is propagated to the client and when it is executed.  Now, we can return to more examples of how to implement validation logic within your application.  We learned in this post that validation isn’t limited to properties and that you can create entity-level validators; in the next post, we’ll explore entity-level validation and illustrate some cross-field validation scenarios and approaches.  Beyond that, we will learn more about ValidationContext and ViewModel validation.