Please note… this post is out-dated and all of the madness below is no longer needed in order to implement Async Validation with RIA Services.  Please see the new post for the current approach.

The Birth of QuickSilverlight.Validation

Over on the .NET RIA Services forums, “SilverlightRIA” asked about performing asynchronous validation with .NET RIA Services and the Silverlight Toolkit’s DataForm.  He wanted to intercept the Commit from the DataForm and invoke his async validation, integrating the results into the DataForm’s validation UI.  This was an interesting scenario so I put some time into a solution for it this week.  The result is a library that I’m calling QuickSilverlight.Validation.

Integrating Validation Results into DataForm

It took a few hours of digging to find a way to get the async validation results to integrate into the DataForm’s UI.  Here’s what DataForm does when you commit edits:

  1. Loop through all of the fields, calling UpdateSource on each field’s binding.  This will call the setters for all properties, which triggers validation.
  2. If validation fails on the property, the field is highlighted, an item is added to the validation summary, and clicking that summary item will put focus into the affected field.
  3. If validation succeeds on all of the properties, then entity-level validation is invoked.
  4. If entity-level validation succeeds, then the entity is committed.

To integrate async validation results into the DataForm, it was imperative to have the property-level validators fail validation, producing the messages retrieved from the async validation.  Here was my starting idea for how to get this going:

  1. Apply an [AsyncValidation] attribute to the properties that can have async validation results applied to them.
  2. Make the AsyncValidation attribute return a ValidationResult that comes from an async validation execution.

The trick of course is to fetch the async validation results before the properties are validated through DataForm.  Here’s how I pulled that off:

  1. Handle the DataForm.EditEnding event, which is raised before validation occurs.
  2. Within that event handler, call DataForm.ValidateItem() to see if the item is valid with respect to its client-side, synchronous validators.  Make the AsyncValidation attributes return Success at that stage.
  3. If valid, initiate the call to get async validation results, and cancel the EditEnding event using CancelEventArgs.Cancel = true.
  4. This prevents DataForm from progressing on to actually commit the edit.
  5. When async validation completes, store the results for access by the AsyncValidation attributes.
  6. Call DataForm.CommitEdit(), triggering the whole process to start over.
  7. Within the DataForm.EditEnding event handler, recognize that async validation result processing is in process, and don’t do anything to change the DataForm’s behavior.  Let it validate the properties and object as it normally would, committing the entity if everything is valid.
  8. At this stage, make the AsyncValidation attributes respect the async validation results, generating validation errors as appropriate.
  9. When this is finished, clear any stored async validation results for the entity, so that the next time it’s validated, our async validation results won’t interfere with client-side synchronous validation.

Yep; that was a mouthful.  But I have two flavors of good news.  First, I am going to make the source code for all of this available.  Second, I’ve packaged the code up into a library that allows you to get all of this behavior for next to nothing.  We’re actually not yet finished though, as there were a few more details to work out to finish the end-to-end solution.

.NET RIA Services Integration

With .NET RIA Services, we want to apply the [AsyncValidation] attribute to the entities on the server, and have that metadata flow through to the client.  We also want the same validation to execute on the server, but synchronously.  Let’s first look at executing the validation on the server.

.NET RIA Services includes an updated System.ComponentModel.DataAnnotations assembly.  One of the new features in that assembly with the July 2009 Preview is an interface called IValidatableObject.  This interface only applies to the desktop version of this assembly—it’s not included in the Silverlight subset of the assembly.  Here’s the definition of the interface:

   1: namespace System.ComponentModel.DataAnnotations
   2: {
   3:   public interface IValidatableObject
   4:     {
   5:         IEnumerable<ValidationResult> Validate(ValidationContext validationContext);
   6:     }
   7: }


The great part about this interface is that it allows not just a single ValidationResult, but an IEnumerable<ValidationResult> to be returned from the Validate method.  .NET RIA Services respects this interface when it performs server-side validation of your entities, so all we need to do is implement this interface on our entity, and collect our results from within the Validate method.  Instead of calling our process asynchronously, we call it synchronously, and we return the list of results.

Now, for applying the [AsyncValidation] attribute to our entities, I’ve created a server-side AsyncValidationAttribute class.  Interestingly, this attribute doesn’t derive from ValidationAttribute like our Silverlight attribute did.  On the server, this attribute isn’t going to perform any validation, since it’s all done in one step for IValidatableObject.  So, this attribute is just a marker to allow metadata to flow through to the client-side proxy classes.  The namespace and class name match what’s on the client, and .NET RIA Services will automatically flow this metadata through for us.

Calling ValidateProperty() for Async Validation

Things seem to be rolling pretty smoothly at this point, but there’s one problem left that is preventing this from working.  This whole idea is based on the DataForm calling UpdateSource on each field’s binding, and having that trigger property-level validation.  But this doesn’t work because of the way the entity’s property setters are generated in the client-side proxies.  Here’s a sample property setter that .NET RIA Services generates, with our [AsyncValidation] attribute nicely applied:

   1: [AsyncValidation()]
   2: [DataMember()]
   3: public char Gender
   4: {
   5:     get
   6:     {
   7:   return this._gender;
   8:     }
   9:     set
  10:     {
  11:   if ((this._gender != value))
  12:         {
  13:   this.ValidateProperty("Gender", value);
  14:   this.OnGenderChanging(value);
  15:   this.RaiseDataMemberChanging("Gender");
  16:   this._gender = value;
  17:   this.RaiseDataMemberChanged("Gender");
  18:   this.OnGenderChanged();
  19:         }
  20:     }
  21: }


The detail that tripped me up is that ValidateProperty() is only called if the property value has changed.  But in our case, we trigger the UpdateSource() calls twice – once for the standard validation, then a second time after we receive the async validation results.  Upon the second execution of this, the property value hasn’t changed, and ValidateProperty() is not called.  Somehow, we need to force ValidateProperty() to get called for each field displayed by the DataForm.  CodeProcessor (and my co-worker Warren Aldred) to the rescue!

.NET RIA Services allows you to decorate your DomainService with a CodeProcessor.  This CodeProcessor can hook into the code generation for the client-side proxies, and manipulate the CodeDom of the generated code.  We want to manipulate any properties marked with [AsyncValidation] so that the property looks like this:

   1: [AsyncValidation()]
   2: [DataMember()]
   3: public char Gender
   4: {
   5:     get
   6:     {
   7:   return this._gender;
   8:     }
   9:     set
  10:     {
  11:   // Validate this property even if the value hasn't changed, so that the async validation can occur properly
  12:   this.ValidateProperty("Gender", value);
  13:  
  14:   if ((this._gender != value))
  15:         {
  16:   this.OnGenderChanging(value);
  17:   this.RaiseDataMemberChanging("Gender");
  18:   this._gender = value;
  19:   this.RaiseDataMemberChanged("Gender");
  20:   this.OnGenderChanged();
  21:         }
  22:     }
  23: }


It’s a very slight modification.  We just move the ValidateProperty() call to happen before we check for a value change.  And the good news is that I have packaged this CodeProcessor up with my QuickSilverlight.Validation library, so you don’t even need to mess with CodeDom.  I built an AsyncValidationCodeProcessor that can be used either directly, or you can compose its functionality into another CodeProcessor if you’re already using one.  I respected composition over inheritance here, so that you could apply async validation even if you’re already manipulating the CodeDom.

Applying [AsyncValidation] to an Entity

Since I was manipulating the CodeDom, I figured I might as well add one more feature related to the [AsyncValidation] attribute too.  If you want to turn async validation on for all of your entity’s properties, you can simply apply the [AsyncValidation] attribute to the entity class itself, and the AsyncValidationCodeProcessor will explode this onto all properties that make calls to ValidateProperty().

How to Use QuickSilverlight.Validation

Now that we have designed a working solution, let’s look at what you need to do to get async validation working on your entity, using QuickSilverlight.Validation and the AdventureWorks Employee entity.  For my example, I added a stored procedure to the AdventureWorks database:

   1: ALTER PROCEDURE dbo.GetEmployeeValidationErrors
   2:     @gender char(1)
   3: ,    @maritalStatus char(1)
   4: AS
   5:   
   6:   SELECT    FieldName = 'Gender', ErrorMesssage = 'The two valid values are ''M''/''F'''
   7:   WHERE    @gender IS NOT NULL AND @gender NOT IN ('M', 'm', 'F', 'f')
   8:  
   9:   UNION
  10:   SELECT    FieldName = 'MaritalStatus', ErrorMessage = 'The two valid values are ''S''/''M'''
  11:   WHERE    @maritalStatus IS NOT NULL AND @maritalStatus NOT IN ('S', 's', 'M', 'm')
  12:   
  13:   RETURN

 

I then expose this stored procedure through a Silverlight-enabled WCF service, creating a ValidationService.svc.

   1: [ServiceContract(Namespace = "AsyncValidationSample.Web.ValidationService")]
   2: [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
   3: public class ValidationService
   4: {
   5:     [OperationContract]
   6:   public List<GetEmployeeValidationErrorsResult> ValidateEmployee(char gender, char maritalStatus)
   7:     {
   8:         var context = new AdventureWorksDataContext();
   9:   return context.GetEmployeeValidationErrors(gender, maritalStatus).ToList();
  10:     }
  11: }

Step 1: Decorate the DomainService

We need to apply the CodeProcessor to the DomainService.  This is done through a simple attribute, specifying the CodeProcessor type.

   1: [EnableClientAccess()]
   2: [DomainIdentifier("EmployeeService", CodeProcessor = typeof(AsyncValidationCodeProcessor))]
   3: public class EmployeeService : LinqToSqlDomainService<AdventureWorksDataContext>
   4: {
   5:     ...
   6: }


If you are already using a custom CodeProcessor, then just hook the 2 together by calling the AsyncValidationCodeProcessor.ProcessAsyncValidationProperties method from within your CodeProcessor.

Step 2: Implement IValidatableObject

Using a partial class for your entity on the server, implement this interface to get validation results to be returned as part of the entity-level validation.  This is where I call my ValidationService.ValidateEmployee method from the server-side validation.

   1: public partial class Employee : IValidatableObject
   2: {
   3:   public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
   4:     {
   5:   return from error in new ValidationService().ValidateEmployee(this.Gender, this.MaritalStatus)
   6:                select new ValidationResult(error.ErrorMesssage, new string[] { error.FieldName });
   7:     }
   8: }


Step 3: Apply [AsyncValidation]

You can apply this attribute either to the entity class or to specific properties.  In my case, I am only producing async validation errors for the Gender and MaritalStatus properties, so I will only apply the attribute to those properties.  Using the EmployeeMetadata class, I apply the attribute to the 2 fields that correspond to the desired properties.

   1: internal sealed class EmployeeMetadata
   2: {
   3:     [AsyncValidation]
   4:   public char Gender;
   5:  
   6:     [AsyncValidation]
   7:   public char MaritalStatus;
   8: }

 

(I’ve trimmed out the unrelated code from this snippet)

Step 4: Implement IAsyncValidatableObject on the Client

This article hasn’t yet discussed the details for how an entity on the client will actually perform its async validation.  For the most part, this will be domain specific.  But the solution integrates with your domain-specific validation by using an interface called IAsyncValidatableObject.  Note that this interface is NOT part of DataAnnotations, but rather a custom interface created solely for QuickSilverlight.Validation.

   1: namespace QuickSilverlight.Validation
   2: {
   3:   /// <summary>
   4:   /// Interface to indicate that an object supports asynchronous validation.
   5:   /// </summary>
   6:   public interface IAsyncValidatableObject
   7:     {
   8:   /// <summary>
   9:   /// Method called to invoke the asynchronous validation operation.
  10:   /// </summary>
  11:   /// <param name="userState">
  12:   /// State information that must be provided to the <see cref="AsyncValidationCompletedEventArgs"/>
  13:   /// used on the <see cref="AsyncValidationCompleted"/> event.
  14:   /// </param>
  15:   void ValidateAsync(AsyncValidationState validationState);
  16:  
  17:   /// <summary>
  18:   /// The event to raise when asynchronous validation is completed.  The
  19:   /// <see cref="AsyncValidationCompletedEventArgs"/> must be given the validationState provided to
  20:   /// the <see cref="ValidateAsync"/> method.
  21:   /// </summary>
  22:   event EventHandler<AsyncValidationCompletedEventArgs> AsyncValidationCompleted;
  23:     }
  24: }


Here’s how my Employee class implements the interface, calling the same WCF service that was called from IValidatableObject on the server.

   1: public partial class Employee : IAsyncValidatableObject
   2: {
   3:   private ValidationService.ValidationServiceClient _validationService;
   4:  
   5:   /// <summary>
   6:   /// Begin Async validation by calling our WCF service method.
   7:   /// </summary>
   8:   /// <remarks>Explicitly implement the interface method.</remarks>
   9:   /// <param name="validationState">The validation state to pass through to the <see cref="AsyncValidationCompleted"/> event.</param>
  10:   void IAsyncValidatableObject.ValidateAsync(AsyncValidationState validationState)
  11:     {
  12:   if (this._validationService == null)
  13:         {
  14:   this._validationService = new AsyncValidationSample.ValidationService.ValidationServiceClient();
  15:   this._validationService.ValidateEmployeeCompleted += ValidateEmployeeCompleted;
  16:         }
  17:  
  18:   this._validationService.ValidateEmployeeAsync(this.Gender, this.MaritalStatus, validationState);
  19:     }
  20:  
  21:   /// <summary>
  22:   /// Handle the completion of our WCF service call, translating the results into our async validation results, and
  23:   /// then calling the callback supplied to <see cref="IAsyncValidationEntity.ValidateAsync"/>.
  24:   /// </summary>
  25:   /// <param name="sender">The event sender.</param>
  26:   /// <param name="e">The event args for the event.</param>
  27:   private void ValidateEmployeeCompleted(object sender, ValidateEmployeeCompletedEventArgs e)
  28:     {
  29:         IEnumerable<ValidationResult> results = e.Result.Select(r => new ValidationResult(r.ErrorMesssage, new string[] { r.FieldName}));
  30:   this.AsyncValidationCompleted(this, new AsyncValidationCompletedEventArgs(results, e.UserState as AsyncValidationState));
  31:     }
  32:  
  33:   public event EventHandler<AsyncValidationCompletedEventArgs> AsyncValidationCompleted = delegate { };
  34: }


There are a few important points to make about this code:

  1. ValidateAsync receives an AsyncValidationState object.  This object is required to be passed into the AsyncValidationCompletedEventArgs constructor.  This is the glue that holds the end-to-end process together.
  2. AsyncValidationCompletedEventArgs accepts the IEnumerable<ValidationResult> produced by your validation.  So you must project whatever type of results you get into an enumerable of ValidationResult instances.  Be sure to provide the array of member names as the second parameter in the ValidationResult constructor.
  3. I chose to implement ValidateAsync explicitly, so that it wouldn’t clutter the API for my Employee entity.

Step 5: Enable Async Validation on the DataForm

As mentioned very early on, this solution relies on subscribing to the EditEnding event of the DataForm.  Attached Behaviors save the day here.  The QuickSilverlight.Validation library includes an AsyncValidation class that takes care of all of the logic described for bending the DataForm to our will.  This class includes an attached property called Enabled.  By setting it to true for a DataForm, it will subscribe to the EditEnding event and ensure that the async validation is processed.

Here’s what you need to do in your XAML to enable async validation for a DataForm:

   1: <df:DataForm x:Name="EmployeeForm" AutoCommit="False"
   2:  qsv:AsyncValidation.Enabled="True" />


Yes, seriously, that’s it!  Just set AsyncValidation.Enabled=”True” and the DataForm will behave as we need.  Of course, setting Enabled=”False” will disable the behavior by unsubscribing from the EditEnding event.  There’s no code required in the code-behind to make this work either.

QuickSilverlight.Validation Library

This library includes 2 assemblies: QuickSilverlight.Validation for the Silverlight side of things; and QuickSilverlight.Validation.Web for the server side of things, to integrate with .NET RIA Services.  On the client side, there’s no dependency on .NET RIA Services.  So this can also be used for classes that were not generated by .NET RIA Services.

The source code, binaries, and my example can be found here:
QuickSilverlight.Validation.zip