-
Notifications
You must be signed in to change notification settings - Fork 4
Document Validation
The Mutators library provides a way to define document validations. By a document we mean a C# class with some fileds and/or properties that in turn can be classes, structures, arrays or basic C# language types. For example:
public class UserData
{
public string UserId { get; set; }
public ContactInfo ContactInfo { get; set; }
public string[] UserGroupIds { get; set; }
}
public class ContactInfo
{
public string Email { get; set; }
public string Phone { get; set; }
}
In order to use Mutators validation mechanics we should create a class derived from the abstract class DataConfiguratorCollectionBase<TData>
with the UserData
class as it's generic type parameter.
Then we should provide an implementation for the abstract method Configure
.
In this method you can set validation rules using the MutatorsConfigurator
parameter.
public class UserDataConfiguratorCollection : DataConfiguratorCollectionBase<UserData>
{
public UserDataConfiguratorCollection(IDataConfiguratorCollectionFactory dataConfiguratorCollectionFactory,
IConverterCollectionFactory converterCollectionFactory,
IPathFormatterCollection pathFormatterCollection)
: base(dataConfiguratorCollectionFactory, converterCollectionFactory, pathFormatterCollection)
{
}
protected override void Configure(MutatorsContext context, MutatorsConfigurator<UserData> configurator)
{
configurator.Target(x => x.UserId)
.Required()
.NotLongerThan(18);
var contractInfoConfigurator = configurator.GoTo(x => x.ContactInfo);
contractInfoConfigurator.Target(x => x.Email)
.InvalidIf(x => !x.Email.Contains("@"),
x => new InvalidEmailText())
.RequiredIf(x => string.IsNullOrEmpty(x.Phone));
configurator.Target(x => x.UserGroupIds.Each())
.InvalidIf(x => x.UserGroupIds.Count(id => x.UserGroupIds.Current() == id) > 1,
x => new UserGroupIdMustBeUniqueText());
}
}
We use the configurator's method Target
to specify a path to a property that we want to validate.
After the Target
method we can call one or more validation methods such as RequiredIf
and InvalidIf
.
You can see all available validation methods in the MutatorsConfiguratorExtensions or you can implement your own validation method.
These methods take a lambda expression from TData
to bool?
as a condition
parameter.
If the condition is satisfied, then the corresponding validation result will have type 'Error', if the condition is not satisfied, then the result will have type 'Ok'.
If a target property is absent then no validation result will be produced.
The DataConfiguratorCollectionBase
class exposes the public method GetMutatorsTree
, which returns the MutatorsTreeBase<TData>
class.
Meaning of this class is not of our interest at the moment, it is suffice to say that this class is an intermediate representation of the validations we set and we can obtain a validation function from it by calling the GetValidator
method.
The validation function returned by the GetValidator
method returns a ValidationResultTreeNode
value.
It follows that the result of a document validation is a tree reflecting the document structure and the GetValidator
method returns the root of that tree.
Each node of the tree contains the result of all validations bound to this node.
You don't need to worry about the validation result tree because ValidationResultTreeNode
implements C# iterator pattern, so you can obtain the IEnumerable<FormattedValidationResult>
.
The FormattedValidationResult
class has properties Type
, Message
, Path
and other properties with information about executed validations.
Goto
is a method which helps you get rid of a boilerplate code.
Instead of writing long and slightly different paths to properties from the root of the document, you can travel to a node of the document tree and define validation rules for the subtree of this node.
The Goto
method returns a new MutatorsConfigurator
instance, which "remembered" a path to a node of the document tree.
All configurator methods called on the new instance will take as parameters paths going from the corresponding document subtree root.
Mutators give you ability to set validations for array items (see the last validation in the example above).
To provide a path to an array item you can use the method Each
called on an array property.
It also allows you to use the Current
method on an array property inside a condition of a validation method, which means that you accessing the same index of the array as the index of the array in the Target
method (as in the example above).
Some validation methods (for example the InvalidIf
method) require a message
parameter, which is a lambda expression returning a class inherited from the MultiLanguageTextBase
class.
This class is meant to provide a validation message for multiple languages, you can see ValueMustBeEqualToText as an example.
The MutatorsContext
is a parameter of the Configure
method, which is used to customize a validation ruleset. See more on MutatorsContext page.
The If
method takes a condition (represented by a lambda expression) and returns a new MutatorsConfigurator
instance which "remembered" this condition. It will be added (using logical 'and') to all conditions of validations that was set using this instance (or instances created from this instance).
You can use If
method in case when you need to set multiple validations that have some common part in their conditions.
This parameter is not required for simple document validation, you can pass null
as the corresponding constructor parameter. See more on [DataConfiguratorCollectionFactory] page.
This parameter is not required for simple document validation, you can pass null
as the corresponding constructor parameter. See more on [ConverterCollectionFactory] page.
The IPathFormatterCollection
interface encapsulates a fabric that will be called to obtain the IPathFormatter
.
IPathFormatter
is an abstraction for a class that generates a code which will be executed to build a path to a property under validation.
You can simply pass a new PathFormatterCollection
instance to the DataConfiguratorCollection
constructor.
It will use SimplePathFormatter
class for any type you validate.
It concatenates names of properties in a path with dots (.) and uses square brackets to denote array indices ([index]), for example: UserData.UserGroupIds[2]
.