Custom Validation

All too often with validation approaches, standard validation like required fields and formatting are easy, while custom validation requires significantly more effort.  With Exo, standard validation is extremely easy--primarily enabled by applying simple data annotations to the model.  However, Exo also makes custom validation extremely easy by supporting custom model validation rules and extending the model to track and expose conditions like errors and warnings.  The following is an example of custom validation rule that asserts an error condition in the model:

/// <summary>

/// Ensures that the due date is on or after the day on which the item was created.

/// </summary>

static Error ValidateFutureDueDate = new Error<ListItem>(

       "The Due Date must be today or some time in the future",

       item => item.DateCreated != null && item.DueDate != null && item.DueDate.Value < item.DateCreated.Value.Date);

This simple single statement defines a unique error condition, ListItem.ValidateFutureDueDate, the error message to display to the user, and a condition expression that asserts when the error exists.  This strongly-typed Error instance creates a rule that will run automatically when either the DateCreated or DueDate changes.  Like with calculated property rules, Exo supports automatically projecting server rules up to the client as Javascript rules, so this rule will also automatically run on the client in response to changes to the client model.  As with the server model, the client model raises events when conditions in the model change, thereby allowing the declaratively linked UI to automatically update and display or hide the error as necessary, using the exact same approach used for standard validation rules like Required or StringFormat

The following FireBug trace shows this rule being sent as part of the type JSON metadata to the client:

This is all the information the client needs to create a corresponding rule asserting the same error on the client as defined on the server.  While this was a simple rule that just runs on property change and is limited to properties on the same entity, rules can be much more complex, such as:

/// <summary>

/// Ensure that items in the same list have unique text descriptions.

/// </summary>

static Error ValidateUniqueText = new Error<ListItem>(

       "This appears to be a duplicate!",

       item => item.List != null && item.List.Items.Any(i => i != item && i.Description == item.Description),

       "Description")             // Only attach the error to the Description property on the root instance

       .OnInitExisting();         // Ensure validation runs on existing instances since this rule is not enforced

This rule goes further by performing validation against all of the items within a list to ensure uniqueness.  This rule specifically specifies that the error should only be associated with the item being validated, as by default the error would be attached to all entities and properties the rule interacts with.  Also, this rule is configured to run when existing instances are initialized (loaded from the database or persistence medium), assuming that existing saved items may have this error condition.  This ensures that the UI, when initially rendered via templates on the server, will already show the error state and the model sent from the server will already contain the error.

The validation rule projects to the client as follows:

This rule automatically runs on both client and server whenever items are added or removed from a list or  the description of an item changes, as denoted by the onChangeOf attribute in the JSON.  Exo automatically determines the property path dependencies for expression-based server rules like this, eliminating the guess work and mistakes regarding when to trigger validation.  With Exo the model is always up to date, always validated, always consistent, both client and server, statelessly, seemlessly.


  1. Insert the following code snippet into ListItem.cs:
    				/// <summary>
    				/// Ensures that the due date is on or after the day on which the item was created.
    				/// </summary>
    				static Error ValidateFutureDueDate = new Error<ListItem>(
    					"The Due Date must be today or some time in the future",
    					item => item.DateCreated != null && item.DueDate != null && item.DueDate.Value < item.DateCreated.Value.Date);
    
    				/// <summary>
    				/// Ensure that items in the same list have unique text descriptions.
    				/// </summary>
    				static Error ValidateUniqueText = new Error<ListItem>(
    					"This appears to be a duplicate!",
    					item => item.List != null && item.List.Items.Any(i => i != item && i.Description == item.Description),
    					"Description")			// Only attach the error to the Description property on the root instance
    					.OnInitExisting();		// Ensure validation runs on existing instances since this rule is not enforced