




















































(For more resources on Silverlight, see here.)
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:
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.
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.
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:
To validate the checksum of the CreditCard field, follow these steps:
public class ClientCustomValidation
{
public static ValidationResult ValidMasterCard(string
strcardNumber)
}
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.
[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;
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: