Saturday, August 21, 2010

Silverlight RIA Data Validation Best Practices

Silverlight Data and Controls Series

Introduction

I have been building a pretty heavy forms Silverlight application recently and I wanted to share some simple approaches on how you can handle data validation. There are really tons of options and you can potentially get a lot for free. I am using Silverlight 4 with RIA Services to convert a large scale tradition rich client application to be web based. If we did not have a technology like Silverlight it would be pretty much impossible to build this application without creating significant amounts of Javascript and AJAX – both of which I just plain avoid in all my solutions because of the long term development and maintenance cost of them. Silverlight is what we have wanted since ASP.net can out.

Back to Silverlight validation; there are a couple options you have:

  1. Declarative - Validation Attribute on Property
  2. Custom Attribute Validation (Class or Property)
  3. RIA Service Validation
  4. Silverlight Client Validation (and Asynchronous Validation)

Note that I had to get an understanding of much of this because I had to build a custom RIA Service to hook into a large, legacy non-Microsoft bend database what was not going to change anytime soon. I could not benefit from much of the free stuff you get with Entity. In my case, I create custom DTOs that get filled in the service layer and then passed through a RIA Service to the Silverlight Application.

In this blog I will cover the pros and cons of each and provide simple examples to get you started.

1 - Declarative Validation - Validation Attribute on Property

This is probably the most simple and easiest validation you have in your arsenal. Much of the validations you have are located within System.ComponentModel.DataAnnotations. There are several however the ones that I effectively have used the most are:

  • Key – Enforces uniqueness.
  • Range – Sets a range of values that numeric type can be in.
  • RegularExpression – Regular expression applied to a property.
  • Required – Indicates if a value must be present.
  • StringLength – Indicates the length that a string type property can be.
  • Editable – Indicates if the property can be edited.

Here is a simple example. As you can see in this example I have some of the most basic validations I need.

public class Employee
{
[Key]
public int ID { get; set; }

[Required]
public DateTime DOB { get; set; }

[Required]
[RegularExpression(@"^(?!000)([0-6]\d{2}7([0-6]\d7[012]))([ -]?)(?!00)\d\d\3(?!0000)\d{4}$")]
public string SSN { get; set; }

[Required]
[StringLength(20)]
public string FirstName { get; set; }

[Required]
[StringLength(30)]
public string LastName { get; set; }
}

Pros:

  • For Free - These validations pretty much kick in for free once you bind your object instance to a control in Silverlight. If any of these rules are violated when a value is set, the user will be displayed a message and the field will be highlighted in Red.
  • No RIA Round Trips - If any of the validations have been violated, the user will not be able to submit changes back to the RIA Services. This is good because there is no round trip involved to RIA for the validations to kick in. The validations are actually projected into the Generated_Code of the DTO class type in the Silverlight Application.

Cons:

  • Basic Only - You only get very basic validations.

2 - Custom Attribute Validation (Property or Class Level)

Custom validations can be created and the applied to the class in the similar manner as the declarative manner described above. You will quickly need to do something like this so that you can perform custom validations across multiple fields or against the whole class.

To implement you need to create a class and name the class file XXX.Shared.cs to the location where you have your DTOs defined. Then add some validation logic and reference as CustomValidation attribute on the DTO.

You will notice after you do a build, a copy of the XXX.Shared.cs file will be copied to the Generated_Code folder in the Silverlight Application. What this basically means is that the validation will be run on the Silverlight Application side and not require a round trip to RIA.

Here is an example where I have:

  • I have a validation called IsDOBValid which will validate the date.
  • I have a validation called IsPostEmployeementDateValid which will check to see PostEmployeementDate is correct.
  • I also have an IsActiveValid which performs a similar validation.

I then have IsEmployeeValid which is a class level validation. I have no implementation because I could not come up with a good example but I wanted to show you that it can be used.

using System;
using System.ComponentModel.DataAnnotations;

namespace SilverlightApplication2.Web
{
public class EmployeeValidation
{
public static ValidationResult IsEmployeeValid(Employee employee, ValidationContext context)
{
//Place in Employee Scope validations
return ValidationResult.Success;
}

public static ValidationResult IsDOBValid(DateTime dob, ValidationContext context) {

if (dob == DateTime.MinValue)
{
return new ValidationResult("The Employee DOB is set.",
new string[] { "DOB" });
}

if (dob >= DateTime.Now)
{
return new ValidationResult("The DOB cannot be greater than today.",
new string[] { "DOB" });
}

return ValidationResult.Success;
}

public static ValidationResult IsPostEmployeementDateValid(DateTime postEmployeementDate, ValidationContext context)
{

if (context.ObjectInstance != null)
{
Employee employee = context.ObjectInstance as Employee;
return ValidatePostEmployeementDate(postEmployeementDate, employee.Active);
}

return ValidationResult.Success;
}

public static ValidationResult IsActiveValid(bool active, ValidationContext context)
{

if (context.ObjectInstance != null)
{
Employee employee = context.ObjectInstance as Employee;
return ValidatePostEmployeementDate(employee.PostEmployeementDate, active);
}

return ValidationResult.Success;
}

private static ValidationResult ValidatePostEmployeementDate(DateTime postEmployeementDate, bool active)
{
if (active == false && (postEmployeementDate == null postEmployeementDate == DateTime.MinValue))
{
return new ValidationResult("The Post Employeement Date must be set if the Employee is inactive.",
new string[] { "PostEmployeementDate", "Active" });
}

return ValidationResult.Success;
}

}
}

One observation is I use the value that is passed in to do the validation and not the value in the Employee object when the validation is used on a property. For instance on IsActiveValid I use active and not employee.Active. This is because the value passed in is the value that changed and employee.Active has the old value. This is actually helpful if you need to check to see how the values have changed.

The following modifications to the DTO I created above:

  • First I put the class level validation at the Employee class level.
  • Second I put the three property level validations.
[CustomValidation(typeof(EmployeeValidation), "IsEmployeeValid")]
public class Employee
{
[Key]
public int ID { get; set; }

[Required]
[CustomValidation(typeof(EmployeeValidation), "IsDOBValid")]
public DateTime DOB { get; set; }

[Required]
[RegularExpression(@"^(?!000)([0-6]\d{2}7([0-6]\d7[012]))([ -]?)(?!00)\d\d\3(?!0000)\d{4}$")]
public string SSN { get; set; }

[Required]
[StringLength(20)]
public string FirstName { get; set; }

[Required]
[StringLength(30)]
public string LastName { get; set; }

[Required]
[CustomValidation(typeof(EmployeeValidation), "IsActiveValid")]
public bool Active { get; set; }

[Required]
public DateTime HireDate { get; set; }

[CustomValidation(typeof(EmployeeValidation), "IsPostEmployeementDateValid")]
public DateTime PostEmployeementDate { get; set; }
}

There are several things you should know about these before jumping in and using them across the board. I recommend using these validations when you have simple/medium level validations you need to do.

Pros:

  • Medium Complex Validations - These are great because they do allow you to put in medium complexity level sort of validations for your objects which was missing from the first example I presented.
  • User Notification - Validations will be fired immediately when a value is set into a property level custom validation.

Cons:

  • Class Level User Notification - Class level validations are executed on the client, however they are only evaluated when the context.SubmitChanges() method is called..
  • DataGrid Limitation - If you are using a DataGird, none validations using this pattern will be fired immediately when a user sets a value. This is because the EndEdit event of the DataGrid row needs to be called first and then these validations will kick in. This will become a problem for you if the user needs to set a value in a grid, press tab and need to see the error immediately.
  • Reference Other Library - Library dependencies are major limitation of this solution. You will be tempted to do a complex validation logic in the XXX.Shared.cs class file (i.e. call out to a database and check something). An example would be to reference a service layer library and call a method on it. Then based on the result send a validation error. This will probably compile in your DTO Library because it may have a reference to the service layer library. However when the XXX.Shared.cs is copied to the Silverlight Application into the Generated_Code folder, the Silverlight Application will not compile. This I because the Silverlight Application does not have a direct reference to you the service layer library DLL. Nor can you fix that because you service layer library DLL is written in straight up .NET code library which a Silverlight Application cannot reference.
  • No Asynchronous Validations – This pattern cannot perform asynchronous validations either. This is again because XXX.Shared.cs is defined in the DTO project library and not in your Silverlight Application. So you cannot use any of the RIA services to assist you with you validations.

3 - RIA Service Validations

If you read my blog on RIA Service Method Lifecycle, there is a method called ValidateChangeSet which you can override and perform validations on just the RIA side. This is called before ExecuteChangeSet which is where much of your database transaction logic will reside.

The code below is not meant to be a good code sample, just so you the basic mechanics for working with overriding the ValidateChangeSet method.

protected override bool ValidateChangeSet()
{
bool isValid = base.ValidateChangeSet();

if (IsSomeValidation() == false) {
isValid = false;
ValidationResult validationResult = new ValidationResult("Blah Blah.", new string[] { "PostEmployeementDate" });
throw new ValidationException(validationResult, null, null);
}

return isValid;
}

Pros:

  • Fail Safe Errors – If you have some validations that need to always occur and you want to make sure 100% they are evaluated before any commits occur, this is the place to put them.

Cons:

  • User Will Not Immediately See – The user will not immediately see these errors and have to wait for a RIA round trip.

4 - Silverlight Client Validation (and Asynchronous Validation)

I have found through my Silverlight development that I have ultimately settled on doing validations this way more often than not. If you recall, the Employee DTO definition was projected by RIA to the Generated_Code folder.

If you go look in the generated class, you will see that all of the Employee DTO properties look similar to the following:

/// <summary>
/// Gets or sets the 'DOB' value.
/// </summary>
[CustomValidation(typeof(EmployeeValidation), "IsDOBValid")]
[DataMember()]
[Required()]
public DateTime DOB
{
get
{
return this._dob;
}
set
{
if ((this._dob != value))
{
this.OnDOBChanging(value);
this.RaiseDataMemberChanging("DOB");
this.ValidateProperty("DOB", value);
this._dob = value;
this.RaiseDataMemberChanged("DOB");
this.OnDOBChanged();
}
}
}

Here you see I pulled out the DOB property of the Employee class that is in the Generated_Code folder. There are some interesting things here you should be aware of:

  • OnDOBChanging it a method that can be overridden to perform a task before the values is actually set.
  • RaiseDataMemeberChange is a notification that the DOB value is about to be changed.
  • ValidateProperty is a method that will execute all of the declarative validations (which I discussed earlier). If it fails, it will not continue.
  • Next the value is set.
  • RaiseDataMemberChanged is a notification that the value has changed. This will actually part of the INotifyPropertyChanged interface implementation and will notify the UI binding with the updated value.
  • OnDOBChanged is a method you can override after the value has been set.

In this pattern we will simply extend the generated code by creating a partial class so that the code we create does not get lost every time the code the solution is built:

  • First create a matching partial class in the Silverlight Application for the DTO.
  • Next you override the OnXXXChanging or OnXXXChanged methods.

Here is a simple example:

using System;
using System.ServiceModel.DomainServices.Client;
using System.ComponentModel.DataAnnotations;

namespace SilverlightApplication2.Web
{
partial class Employee
{
ValidationResult someValidationResult;

partial void OnPostEmployeementDateChanged()
{
//Call RIA Method
SimpleContext _simpleContext = new SimpleContext();
_simpleContext.SomeValidation(this.PostEmployeementDate, SomeValidationComplete, null);
}

private void SomeValidationComplete(InvokeOperation<bool> io)
{
if (io.HasError)
{
io.MarkErrorAsHandled();
//do something
}
else
{
if (io.Value)
{
//Remove the error.
if (someValidationResult != null) {
this.ValidationErrors.Remove(someValidationResult);
someValidationResult = null;
}
}
else {
//There was an error
someValidationResult = new ValidationResult("Error Message.", new string[] { "PostEmployeementDate" });
this.ValidationErrors.Add(someValidationResult);
}
}
}
}
}

In the code you will see that:

  • I called a simple RIA Method that returns a bool.
  • I override the OnPostEmployeementDateChanged method and you will note that I did not have to make it is override. All I did was mark it as partial. Now this will be called after a value has been successfully set into the PostEmployeementDate property.
  • In the call back, I check the results and will add a ValidationResult if there is an error.

Pros:

  • Asynchronous Validation – This is a huge plus versus the other validation patterns. This is needed when you got to check an input value against some complex routine which may be database driven.
  • Extending Your DTOs for the UI – This is a great solution because what you can do is define DTOs in the RIA layer with no functionality. Then in the Silverlight layer you can extend your DTOs to have much more functionality and business logic that is only associated to the Silverlight application.

Cons:

  • Validation Not Evaluated on RIA Side – Using the approaches above the validations are fired on both the Silverlight Application side, as well as the RIA side. Obviously the benefit is that your DTOs will always have the rules applied.

Saturday, August 14, 2010

Project Silverlight RIA Methods to Access All Data in Context

Silverlight Data and Controls Series

Background

I have a little trick I have been doing lately with my custom RIA Services to be able to access all the objects that get returned the Context on the Silverlight Application Side. The Context object, which is generated for each RIA Domain Service you have, is a bunch of hash table like collection of objects when you look underneath the hood.

The code generation I just mentioned is commonly referred to as “code projection”. What happens is when you compile your Silverlight application that is using RIA Services you will see a hidden folder called Generated_Code. In there you will find several classes have been generated. First is the context class which is generated for each RIA Domain Service you have. This class will have all the RIA methods you have defined. Second you will see tons of classes in there for each object type that is returned out of the RIA Services. In my case, I have tons of custom DTOs being returned from the RIA methods, so there will be a corresponding type returned.

Here is an example of a RIA method you may create:

[EnableClientAccess()]
public class SimpleService : DomainService
{
public List<ClassA> GetClassAs()
{
return DataService.GetClassAs();
}
}

You will notice that is an extremely simple method and it is returning a collection of DTOs from some data tier. In this example, the ClassA DTO has two encapsulated DTO lists within it (i.e. a one-to-many relationship of data). Here are the definitions to the three DTOs and their relationships to each other:

public class ClassA
{
[System.ComponentModel.DataAnnotations.Key]
public int ID { get; set; }
public string ValueA { get; set; }
private List<ClassB> _classBs = new List<ClassB>();

[Include]
[Association("ClassA_ClassB", "ID", "ClassAID")]
public List<ClassB> ClassBs {
get {
return _classBs;
}
}
}

public class ClassB
{
[System.ComponentModel.DataAnnotations.Key]
public int ID { get; set; }
public int ClassAID { get; set; }
public string ValueB { get; set; }
private List<ClassC> _classCs = new List<ClassC>();

[Include]
[Association("ClassB_ClassC", "ID", "ClassBID")]
public List<ClassC> ClassCs
{
get
{
return _classCs;
}
}
}

public class ClassC
{
[System.ComponentModel.DataAnnotations.Key]
public int ID { get; set; }
public int ClassBID { get; set; }
public string ValueC { get; set; }
}

Then in your Silverlight application, you will call the RIA Service to get your data like the following. As you can see I can access all ClassAs in a LINQ query but I cannot access all of the ClassCs in this manner. In the code below, you will see there is a compile error.

private void GetData() {
_simpleContext = new SimpleContext();
_simpleContext.Load(_simpleContext.GetClassAsQuery(), GetClassAsLoaded, null);
}

private void GetClassAsLoaded(LoadOperation<ClassA> lo)
{
if (lo.HasError)
{
lo.MarkErrorAsHandled();
//do something
}
else
{
//Compiles
var foos = from A in _simpleContext.ClassAs
where A.ValueA == "Foo"
select A;

//Will NOT COMPILE
var bars = from C in _simpleContext.ClassCs
where C.ValueC == "Bars"
select C;
}
}

This was pretty annoying because if I put a breakpoint on context and then do a quick watch I was easily able to view the data. It was even more annoying because RIA enforces that each object instance be unique within the context (SimpleContext in my example). So why can I not just be able to access the list of ClassCs directly to perform an operation. The only solution you would think is to loop over ClassA, then ClassB, and then ClassC.

Solution

The solution I came up with is rather simple. On the RIA service add the following method.

[EnableClientAccess()]
public class SimpleService : DomainService
{
public List<ClassA> GetClassAs()
{
return DataService.GetClassAs();
}

public List<ClassB> GetClassBs()
{
return new List<ClassB>();
}

public List<ClassC> GetClassCs()
{
return new List<ClassC>();
}
}

What this will do is project properties on the context class definition so I can directly access the collection of encapsulated objects. Notice the two new methods have no implementation, nor is any needed either. Now the code above will compile. This example is not really the compelling however when you have a very large custom RIA Service layer you are writing for a big Silverlight application, you will find this very useful.