8 min read

(For more resources on Silverlight, see here.)

Validation

One of the most important parts of the Silverlight application is the correct implementation of validations in our business logic. These can be simple details, such as the fact that the client must provide their name and e-mail address to sign up, or that before selling a book, it must be in stock.

In RIA Services, validations can be defined on two levels:

  • In entities, via DataAnnotations.
  • In our Domain Service, server or asynchronous validations via Invoke.

DataAnnotations

The space named System.ComponentModel.DataAnnotations implements a series of attributes allowing us to add validation rules to the properties of our entities. The following table shows the most outstanding ones:

Validation Attribute

Description

DataTypeAttribute

Specifies a particular type of data such as date or

an e-mail

EnumDataTypeAttribute

Ensures that the value exists in an enumeration

RangeAttribute

Designates minimum and maximum constraints

RegularExpressionAttribute

Uses a regular expression to determine valid values

RequiredAttribute

Specifies that a value must be provided

StringLengthAttribute

Designates a maximum and minimum number

of characters

CustomValidationAttribute

Uses a custom method for validation

The following code shows us how to add a field as “required”:

[Required()]
public string Name
{
get
{
return this._name;
}
set
{
(...)
}
}

In the UI layer, the control linked to this field (a TextBox, in this case), automatically detects and displays the error. It can be customized as follows:

These validations are based on the launch of exceptions. They are captured by user controls and bound to data elements. If there are errors, these are shown in a friendly way. When executing the application in debug mode with Visual Studio, it is possible to find that IDE captures exceptions. To avoid this, refer to the following link, where the IDE configuration is explained: http://bit.ly/riNdmp.

Where can validations be added? The answer is in the metadata definition, entities, in our Domain Service, within the server project. Going back to our example, the server project is SimpleDB.Web and the Domain Service is MyDomainService. medatada.cs. These validations are automatically copied to the entities definition file and the context found on the client side.

In the Simple.DB.Web.g.cs file, when the hidden folder Generated Code is opened, you will be surprised to find that some validations are already implemented. For example, the required field, field length, and so on. These are inferred from the Entity Framework model.

Simple validations

For validations that are already generated, let’s see a simple example on how to implement those of the “required” field and “maximum length”:

[Required()]
[StringLength(60)]

public string Name
{
get
{
return this._name;
}
set
{
(...)
}
}

Now, we will implement the syntactic validation for credit cards (format dddddddd- dddd-dddd). To do so, use the regular expression validator and add the server file MyDomainService.metadata.cs, as shown in the following code:

[RegularExpression(@"d{4}-d{4}-d{4}-d{4}",
ErrorMessage="Credit card not valid format should be:
9999-9999-9999-9999")]
public string CreditCard { get; set; }

To know how regular expressions work, refer to the following link: http://bit.ly/115Td0 and refer to this free tool to try them in a quick way: http://bit.ly/1ZcGFC.

Custom and shared validations

Basic validations are acceptable for 70 percent of validation scenarios, but there are still 30 percent of validations which do not fit in these patterns. What do you do then? RIA Services offers CustomValidatorAttribute. It permits the creation of a method which makes a validation defined by the developer. The benefits are listed below:

  • Its code: The necessary logic can be implemented to make validations.
  • It can be oriented for validations to be viable in other modules (for instance, the validation of an IBAN [International Bank Account]).
  • It can be chosen if a validation is executed on only the server side (for example, a validation requiring data base readings) or if it is also copied to the client.

To validate the checksum of the CreditCard field, follow these steps:

  1. Add to the SimpleDB.Web project, the class named ClientCustomValidation. Within this class, define a static model, ValidationResult, which accepts the value of the field to evaluate as a parameter and returns the validation result.

    public class ClientCustomValidation
    {
    public static ValidationResult ValidMasterCard(string
    strcardNumber)
    }

  2. Implement the summarized validation method (the part related to the result call back is returned).

    public static ValidationResult ValidMasterCard(string
    strcardNumber)
    {
    // Let us remove the "-" separator
    string cardNumber = strcardNumber.Replace("-", "");
    // We need to keep track of the entity fields that are
    // affected, so the UI controls that have this property
    / bound can display the error message when applies
    List<string> AffectedMembers = new List<string>();
    AffectedMembers.Add("CreditCard");
    (...)
    // Validation succeeded returns success
    // Validation failed provides error message and indicates
    // the entity fields that are affected
    return (sum % 10 == 0) ? ValidationResult.Success :
    new ValidationResult("Failed to validate", AffectedMembers);
    }

    To make validation simpler, only the MasterCard has been covered. To know more and cover more card types, refer to the page http://bit.ly/aYx39u. In order to find examples of valid numbers, go to http://bit.ly/gZpBj.

  3. Go to the file MyDomainService.metadata.cs and, in the Client entity, add the following to the CreditCard field:

    [CustomValidation(typeof(ClientCustomValidation),
    "ValidMasterCard")]
    public string CreditCard { get; set; }

If it is executed now and you try to enter an invalid field in the CreditCard field, it won’t be marked as an error. What happens? Validation is only executed on the server side. If it is intended to be executed on the client side as well, rename the file called ClientCustomValidation.cs to ClientCustomValidation.shared. cs. In this way, the validation will be copied to the Generated_code folder and the validation will be launched.

In the code generated on the client side, the entity validation is associated.

/// <summary>
/// Gets or sets the 'CreditCard' value.
/// </summary>
[CustomValidation(typeof(ClientCustomValidation),
"ValidMasterCard")]

[DataMember()]
[RegularExpression("d{4}-d{4}-d{4}-d{4}", ErrorMessage="Credit
card not valid format should be: 9999-9999-9999-9999")]
[StringLength(30)]
public string CreditCard
{

This is quite interesting. However, what happens if more than one field has to be checked in the validation? In this case, one more parameter is added to the validation method. It is ValidationContext, and through this parameter, the instance of the entity we are dealing with can be accessed.

public static ValidationResult ValidMasterCard( string strcardNumber,
ValidationContext validationContext)
{
client currentClient =
(client)validationContext.ObjectInstance;

Entity-level validations

Fields validation is quite interesting, but sometimes, rules have to be applied in a higher level, that is, entity level. RIA Services implements some machinery to perform this kind of validation. Only a custom validation has to be defined in the appropriate entity class declaration.

Following the sample we’re working upon, let us implement one validation which checks that at least one of the two payment methods (PayPal or credit card) is informed. To do so, go to the ClientCustomValidation.shared.cs (SimpleDB web project) and add the following static function to the ClientCustomValidation class:

public static ValidationResult ValidatePaymentInformed(client
CurrentClient)
{
bool atLeastOnePaymentInformed = ((CurrentClient.PayPalAccount !=
null
&& CurrentClient.PayPalAccount != string.Empty) ||
(CurrentClient.CreditCard != null && CurrentClient.CreditCard !=
string.Empty));
return (atLeastOnePaymentInformed) ?
ValidationResult.Success : new ValidationResult("One payment method
must be informed at least");
}

Next, open the MyDomainService.metadata file and add, in the class level, the following annotation to enable that validation:

[CustomValidation(typeof(ClientCustomValidation),
ValidatePaymentInformed")]

[MetadataTypeAttribute(typeof(client.clientMetadata))]
public partial class client

When executing and trying the application, it will be realized that the validation is not performed. This is due to the fact that, unlike validations in the field level, the entity validations are only launched client-side when calling EndEdit or TryValidateObject. The logic is to first check if the fields are well informed and then make the appropriate validations.

In this case, a button will be added, making the validation and forcing it to entity level.

To know more about validation on entities, go to http://bit.ly/qTr9hz.

Define the command launching the validation on the current entity in the ViewModel as the following code:

private RelayCommand _validateCommand;
public RelayCommand ValidateCommand
{
get
{
if (_validateCommand == null)
{
_validateCommand = new RelayCommand(() =>
{
// Let us clear the current validation list
CurrentSelectedClient.ValidationErrors.Clear();
var validationResults = new List<ValidationResult>();
ValidationContext vcontext = new
ValidationContext(CurrentSelectedClient, null, null);
// Let us run the validation
Validator.TryValidateObject(CurrentSelectedClient, vcontext,
validationResults);
// Add the errors to the entities validation error
// list
foreach (var res in validationResults)
{
CurrentSelectedClient.ValidationErrors.Add(res);
}
},(() => (CurrentSelectedClient != null)) );
}
return _validateCommand;
}
}

Define the button in the window and bind it to the command:

<Button
Content="Validate"
Command="{Binding Path=ValidateCommand}"
/>

While executing, it will be appreciated that the fields be blank, even if we click the button. Nonetheless, when adding a breaking point, the validation is shown. What happens is, there is a missing element showing the result of that validation. In this case, the choice will be to add a header whose DataContext points to the current entity. If entity validations fail, they will be shown in this element.

For more information on how to show errors, check the link http://bit.ly/ad0JyD.

The TextBox added will show the entity validation errors. The final result will look as shown in the following screenshot:

LEAVE A REPLY

Please enter your comment!
Please enter your name here