One thing that is not addressed by either of the .NET validation interfaces is the ability to either turn validation on or off, or to set varying levels of validation. This can be useful in several different scenarios, such as having different Views to edit different properties of a data Model object.
An example of this might be having a View that enables users to update the security settings of a User object, where we want to validate that each property has a value, but only for the properties that are currently displayed in the View. After all, there is no point in informing the user that a certain field must be entered if they can't do that in their current View.
The solution is to define a number of levels of validation, in addition to the levels that represent full and no validation. Let's take a look at a simple ValidationLevel enumeration that could fulfill this requirement:
namespace CompanyName.ApplicationName.DataModels.Enums { public enum ValidationLevel { None, Partial, Full } }
As we can see, in this simple example, we just have the three levels of validation, although we could have added many more. However, in practice, we could still manage with this simple enumeration. Let's see how we could use it to implement multi-level validation in our validation base class:
private ValidationLevel validationLevel = ValidationLevel.Full; public ValidationLevel ValidationLevel { get { return validationLevel; } set { if (validationLevel != value) { validationLevel = value; } } } private void Validate(string propertyName, IEnumerable<string> errors)
{
if (ValidationLevel == ValidationLevel.None) return;
UpdateErrors(propertyName, this[propertyName]);
}
We add a ValidationLevel property, with its validationLevel backing field that defaults to the Full enumeration member, as that is the normal action. Then, in the Validate method, we add a new line that simply exits the method if the ValidationLevel property is set to the None enumeration member.
Finally, the developers that use our application framework need to use the ValidationLevel property when validating their properties in the data Model classes. Imagine a scenario where users could edit the names of our products directly in a collection control, or edit all of the product's properties in a separate View. Let's see what our ProductNotify class indexer property would need to look like to demonstrate this:
public override IEnumerable<string> this[string propertyName] { get { List<string> errors = new List<string>(); if (propertyName == nameof(Name)) { if (string.IsNullOrEmpty(Name)) errors.Add("Please enter the product name."); else if (Name.Length > 25) errors.Add("The product name cannot be longer than twenty-five characters."); if (Name.Length > 0 && char.IsLower(Name[0])) errors.Add("The first letter of the product name must be a capital letter."); } else if (propertyName == nameof(Price) && ValidationLevel == ValidationLevel.Full && Price == 0) errors.Add("Please enter a valid price for the product."); return errors; } }
Using our implementation of the INotifyDataErrorInfo interface, we first initialize a string list named errors and then we check the value of the propertyName input parameter. As this implementation enables us to return multiple validation errors per property, we need to take care with our if and else statements.
For example, when the propertyName input parameter equals Name, we have two if statements and one else statement. The first if statement verifies that the Name property has a value, while the else statement checks that its value is no longer than 25 characters.
As these two conditions cannot possibly both be true at the same time, we tie them together with the if...else statement. On the other hand, the product name could be longer than 25 characters and start with a lowercase letter and so, the next condition has its own if statement. In this example, the Name property will be validated when the ValidationLevel property is set to either the Partial or Full members.
However, the remaining condition for the Price property is only to be validated when the ValidationLevel property is set to the Full member and so, that is simply added as a further condition. To trigger partial validation on a data Model variable, we can simply set its ValidationLevel property as follows:
product.ValidationLevel = ValidationLevel.Partial;
Let's now investigate how we can combine the different techniques that we have viewed so far.