-
Notifications
You must be signed in to change notification settings - Fork 5
Validation injection
There are times where you need to inject validation in to a model. In our previous examples, we've been using a User model defined in the Basic model validation with attributes documentation. The model only contains a Password and an Email property. What if you wanted to implement a PasswordConfirmation property?
Let's implement the PasswordConfirmation property in to the same view model used in the Performing Validation section of the document linked to above. Our view model will just have its Execute method updated to inject additional validation in to our model.
public void Execute(object parameter)
{
// Perform validation on the user's built in validation.
this.AppUser.ValidateAll();
// Piggy-back on top of the user default validation with an additional level of validation in our view model.
// We ensure the password and password confirmation matches.
this.AppUser.ValidateProperty(
() => this.PasswordConfirmation.Equals(this.AppUser.Password),
new ValidationErrorMessage("Passwords do not match!"),
"Password");
}
What happens here is pretty simple. We invoke the ValidateProperty method on the model, passing in a delegate and validation failure message. The last parameter is the most important. The last parameter tells the model which property this validation must be associated with. If you are using the built-in Universal Windows App value converters for displaying validation errors on the view, your view will be immediately updated if the view models injected validation fails within the model. Since the view is data-bound to the collection of messages associated with the model, injecting validation in to the model gives us a way to validate and display to the view along-side all of the models built-in validation.
You may also manually add the validation message instead of performing validation. An example would be hitting a web service to see if the data entered is valid. The service calls should be made by your repository or view model, but never the model. To facilitate this, we make the web service call from the view model, then add the failure message to the model. This lets the data-bound view immediately display the failed message, without having any web service calls in the model.
// Perform validation on the user's built in validation.
this.AppUser.ValidateAll();
// Hit a webservice asking if the email is already used.
var service = ServiceFactory.GetService();
bool valid = await service.IsEmailValid(this.AppUser.Email);
// Inject validation failure to the
if (!valid)
{
this.AppUser.AddValidationMessage(
new ValidationErrorMessage("Email address taken."),
"Email");
}
Lastly, there are cases were you want to perform validation on an object, but don't have access to the source. When this occurs, you can build up the validation rules outside of the model, then inject them in.
// Perform validation on the user's built in validation.
this.AppUser.ValidateAll();
// Hit a webservice asking if the email is already used.
var service = ServiceFactory.GetService();
bool valid = await service.IsEmailValid(this.AppUser.Email);
// Inject validation failure to the
if (!valid)
{
var validation = new ValidateObjectHasValueAttribute();
validation.FailureMessage = "Email address already used.";
validation.ValidationMessageType = typeof(ValidationErrorMessage);
this.AppUser.PerformValidation(validation, "Email");
}
Here we instance a validation rule that ensures the email property has a valid value, then perform the validation by passing the validation rule in to the model.