Custom validators
The built-in validators cover common scenarios, but your application will likely need validation logic specific to your domain. Implementing IValidator gives you a consistent pattern that integrates seamlessly with ValidatorCollection.
Implementing IValidator
Here's a validator that checks whether a username meets your application's naming rules:
using MADE.Data.Validation;
public class UsernameValidator : IValidator
{
public string Key { get; set; } = nameof(UsernameValidator);
public bool IsInvalid { get; set; }
public bool IsDirty { get; set; }
public string FeedbackMessage { get; set; } = "Username must be 3-20 characters, letters and numbers only, starting with a letter.";
public void Validate(object value)
{
var username = value?.ToString();
IsInvalid = string.IsNullOrWhiteSpace(username)
|| username.Length < 3
|| username.Length > 20
|| !char.IsLetter(username[0])
|| !username.All(c => char.IsLetterOrDigit(c));
IsDirty = true;
}
}
Use it standalone or as part of a collection:
var validators = new ValidatorCollection
{
new RequiredValidator(),
new UsernameValidator(),
};
validators.Validate(input);
The IValidator contract
When implementing IValidator, follow these conventions:
- Set
IsInvalidtotruewhen validation fails,falsewhen it passes. - Set
IsDirtytotrueafterValidateis called, regardless of the result. This lets consumers distinguish between "not yet validated" and "validated and passed". - Provide a meaningful
FeedbackMessagethat tells the user what went wrong and how to fix it. - Use
Keyto identify the validator in collections. This defaults to the class name but can be customized for scenarios where you use multiple instances of the same validator type.
When to use custom validators vs PredicateValidator
Use PredicateValidator<T> for simple, one-off checks where creating a full class would be overkill:
var ageCheck = new PredicateValidator<int>
{
Predicate = age => age >= 18,
FeedbackMessage = "Must be 18 or older.",
};
Use a custom IValidator class when:
- The validation logic is complex enough to warrant its own tests.
- You'll reuse the validator across multiple forms or endpoints.
- You need to configure the validator with properties (like
MinLengthonMinLengthValidator).
If you build a validator that would be broadly useful, consider contributing it back to MADE.NET.