Click or drag to resize

Inline validation.

This section demonstrates how to make use of the inline validation functionality.

Inline validation is a convenient way to validate small pieces of data, for example the parameters of a method.
With the inline validation functionality you can validate your data "on the fly".
What we mean with this is that it's not required to define classes or configure the framework on forehand to validate pieces of data.
When you include the Xploration.Validation namespace in your source file you are ready to go!

To avoid complex code structures to validate your data, inline validation makes use of the Fluent API.
Altough you could bypass the Fluent API there is no need in doing that. All the needed functionality is provided through the Fluent API.

The examples in this section require you to install the Xploration.Validation nuget package into your project or reference the Xploration.Validation.dll.
After the package is installed or the dll is referenced add a using directive to your source file.

using Xploration.Validation;

Single rule validation.

Lets begin with an example that validates a string variable with a single rule for the length of the string.

// This example checks that the firstName variable does not contain more then 5 chars.

string firstName = "Danny";
// valid becomes true since the length of firstName is <= 5.
bool valid = await firstName.Check().OnMaxLength(5).ValidateAsync();

firstName = "Shirley";
// valid becomes false since the length of firstName is > 5.
valid = await firstName.Check().OnMaxLength(5).ValidateAsync();

If we analyze the code we can see that a fluent statement is used to validate the string variable firstName.
To validate the variable several steps were made:

  1. First we need a variable, argument or property to validate. In this case the firstName variable.

  2. Next we tell the framework that we want to Check() that variable.

  3. Next we tell the framework what we want to check, in this case we added a rule for the maximum length of the string variable.

  4. Last we ask the framework to validate the variable. If the variable is valid true will be returned; otherwise false.

Multiple rules validation.

You can extend a validation statement with extra rules by using the And() statement. There is no limit on the amount of rules you want to apply.

// This example checks that the firstName variable does not contain less then 2 and no more then 5 chars.

string firstName = "Danny";
// Valid becomes true since the length of firstName is >= 2 and <= 5.
bool valid = await firstName.Check().OnMinLength(2).And().OnMaxLength(5).ValidateAsync();

firstName = "A";
// Valid becomes false since the length of firstName is < 2.
valid = await firstName.Check().OnMinLength(2).And().OnMaxLength(5).ValidateAsync();

firstName = "Shirley";
// Valid becomes false since the length of firstName is > 5.
valid = await firstName.Check().OnMinLength(2).And().OnMaxLength(5).ValidateAsync();
In this example the previous example is extended with a minimum string length rule. If we now validate the firstName variable it must meet both rules.
Inspect validation results.

If you want more information on why a variable isn't valid you can request the framework to report back the validation results of each rule.
You can use this information, for example, in an error recovery situation or to produce an extensive error message.

// Single space causes 2 failures; OnNotNullOrWhiteSpace and OnMinLength(2)
string firstName = " ";
IEnumerable<ValidationResult> results = await firstName.Check().OnNotNullOrWhiteSpace().And().OnMinLength(2).And().OnMaxLength(5).ExecuteAsync();

// Did the validation of the firstName variable failed?
if (results.ContainsFailures())
  // Loop through the failed results only.
  foreach (ValidationResult vr in results.Where((r) => !r.IsValid))
    // Process failed result ...

In the above example we validate the firstName variable with several rules. Instead of calling ValidateAsync(), which returns a boolean, we now call ExecuteAsync().
This call results in a collection of ValidationResult objects which we can further process.

Note Note

The validation process returns all validation results, failed and succeeded.

Provide custom errors.

The Validation framework allows you to report back any error entity that is meaningful to your application. It isn't restricted to an error message, although you can certainly do that.

// Single space causes 1 failure; OnNotNullOrWhiteSpace with a string error entity.
string firstName = " ";
// ReturnError accepts any type. In this case we use a string: "Value required" and an int: 555
IEnumerable<ValidationResult> results = await firstName.Check().OnNotNullOrWhiteSpace().ReturnError("Value required").And().OnMaxLength(5).ReturnError(555).ExecuteAsync();

// CastErrors<E> returns all error entities of type E. In this case string entities.
IEnumerable<string> stringErrors = results.CastErrors<string>();

// Loop through the string error entities.
foreach (string error in stringErrors)
  // Process string errors ....

// Loop through the int error entities. (Not present, errors are only assigned when the rule fails)
foreach (int error in results.CastErrors<int>())
  // Process int errors ....

In the above example we provide a string error entity for the OnNotNullOrWhiteSpace rule and an int error entity for the OnMaxLength rule.
The validation process will assign these error entities to the ValidationResult of the related rule if the rule failed.
So if, for example, the OnNotNullOrWhiteSpace rule fails the string "Value required" will be assigned to the ValidationResult of that rule.

Note Note

Error entities are only assigned to a ValidationResult if the rule failed.

Note Note

An error provider is bound to a single rule. The And() statement marks the begining of a new rule and does not use the previously declared error provirder.

Throw an exception on failure.

Instead of acting upon the validation outcome later on you can also immediately throw an exception if validation fails.
This is useful, for example, in the case you want to check a method its arguments.

public async Task SetName(string firstName, string lastName)
  // This statement throws a ValidationException if validation of the firstName argument fails.
  await firstName.Check().OnNotNullOrWhiteSpace().And().OnMinLength(2).And().OnMaxLength(5).ValidateAndThrowAsync();

  // This statement throws an ArgumentException if validation of the lastName argument fails.
  await lastName.Check().OnNotNullOrWhiteSpace().And().OnMinLength(1).And().OnMaxLength(25).ValidateAndThrowAsync<ArgumentException>();

  _firstName = firstName;
  _lastName = lastName;

  // etc...
This example will throw a ValidationException if the firstName argument isn't valid. This exception contains all the ValidationResult objects that failed.
If you want to throw a custom exception you need to supply that custom exception as a generic type parameter to the ValidateAndThrowAsync method.
In the above example an invalid lastName argument will throw an ArgumentException. The supplied generic type parameter overrides the default exception(ValidationException) to be thrown.
Validate collections.

When you want to validate a collection of entities you can validate the collection itself or validate each element in that collection.
If you validate the collection itself you can check on its properties, for example the amount of elements it contains.
To validate the properties of a collection you can use the CheckCollection() statement.

// This example checks the amount of elements in a collection.

// Create a collection of 5 integers.
int[] integers = new int[] { 1, 2, 3, 4, 5 };

// valid becomes true since the amount of elements are >= 1 and <= 5.
bool valid = await integers.CheckCollection().OnMinElements(1).And().OnMaxElements(5).ValidateAsync();

// Create a collection of 6 integers.
integers = new int[] { 1, 2, 3, 4, 5, 6 };

// valid becomes false since the amount of elements are > 5.
valid = await integers.CheckCollection().OnMinElements(1).And().OnMaxElements(5).ValidateAsync();
In the above example we validate the integers variable with several rules. These rules will result in that a collection must contain at least 1 element and at maximum 5 elements.
You can also extend the fluent statment with error providers or throw an exception on failure as in the above examples.
Validate elements.

When you want to validate elements that are contained in a collection you can use the CheckElements() statement.
Rules that are added after this statement will be applied to each element in the collection.

In the following example we are going to use a custom error provider that returns an error message stating that a certain name is to short.
Custom error providers are useful if you want to provide a more detailed error to your application.
Error providers can be objects that implement the IErrorProvider interface.

public class TooShortNameErrorProvider : IErrorProvider
  // IErrorProvider implementation
  public object GetError(ValidationResult result)
    return $"The name: {result.Context.NewValue} is too short.";
// This example checks the string elements in a collection.

// Create a collection of 4 strings.
string[] names = new string[] { "Danny", "Shirley", "Luc", "Rik" };

// Validates the names elements on a maximum string length of 4 returning an error with the help of a custom error provider. 
IEnumerable<ValidationResult> results = await names.CheckElements().OnMinLength(4).ReturnError(new TooShortNameErrorProvider()).ExecuteAsync();

// Loop through the string errors provided by the custom MyErrorProvider class.
foreach (string message in results.CastErrors<string>())
  // Two error messages are returned: "The name: Luc is too short." and "The name: Rik is too short."

// Use a lambda expression as custom error provider. In this case the element index of the failed name is returned.
results = await names.CheckElements().OnMinLength(4).And().OnMaxLength(5).ReturnError((vr) => vr.Context.Index).ExecuteAsync();

// Notice that an element index is provided within the context of a validation result.

// Loop through the int errors provided by the lambda expression.
foreach (int index in results.CastErrors<int>())
  // No (int)error provider is given for the OnMinLength rule.
  // So only one int error is returned by the CastErrors method: index 1 (Shirley > 5)