Simple validation with validators

Datetime:2016-08-22 22:51:44          Topic: Assembler           Share

There are many ways to perform a validation of our models living within the system.

Whether there’s an incoming request from the user who would like to create an account or there’s a need to ensure about the correct amount of money in a bank transaction, the validation process should always (I really mean that) take place. In today’s post, I’d like to present one of the possible solutions that might help you validate your entities.

Let’s start with a definition of the IValidator interface:

public interface IValidator<in T>
{
    void ValidateOrFail(T model);
    IEnumerable<string> BrokenRules(T model);
}

If the validation fails, there will be a ModelValidationException thrown:

public class ModelValidationException : Exception
{
    public IEnumerable<string> ValidationErrors { get; }
 
    public ModelValidationException()
    {
    }
 
    public ModelValidationException(string message) : base(message)
    {
    }
 
    public ModelValidationException(params string[] validationErrors) : this("Validation errors occured.", validationErrors)
    {
    }
 
    public ModelValidationException(string message, params string[] validationErrors) : base(message)
    {
        ValidationErrors = validationErrors;
    }
}

If you’re not a fan of throwing the exceptions in such cases, you might as well return a boolean or IEnumerable in order to check whether there are no error messages – it’s totally up to you, as the outcome shall remain the same.

Now, let’s assume there’s the following class (that would be a ValueObject in the world of DDD ):

public class UserProfile
{
    public string FirstName { get; protected set; }
    public string LastName { get; protected set; }
    public int? Age { get; protected set; }
    public Gender? Gender { get; protected set; }
 
    protected UserProfile(string firstName, string lastName, int? age,
        Gender? gender)
    {
        FirstName = firstName;
        LastName = lastName;
        Age = age;
        Gender = gender;
    }
 
    public static UserProfileCreate(string firstName, string lastName, int? age,
        Gender? gender) => new UserProfile(firstName, lastName, age, gender);
}

And we could define the following profile validator:

public class UserProfileValidator : IValidator<UserProfile>
{
    public void ValidateOrFail(UserProfilemodel)
    {
        var brokenRules = BrokenRules(model).ToArray();
        if(brokenRules.Any())
            throw new ModelValidationException(brokenRules);
    }
 
    public IEnumerable<string> BrokenRules(UserProfilemodel)
    {
        if (model == null)
        {
            yieldreturn "Profile can not be null.";
            yieldbreak;
        }
 
        if (model.Age.HasValue && model.Age < 13)
            yieldreturn $"Age: {model.Age} is not greater than or equal to 13.";
 
        if (model.FirstName?.Length > 100)
            yieldreturn "First name length can not be longer than 100 characters. " +
                        $"You've typed: {model.FirstName?.Length} characters.";
 
        if (model.LastName?.Length > 100)
            yieldreturn "Last name length can not be longer than 100 characters. " +
                        $"You've typed: {model.FirstName?.Length} characters.";
        }
    }
}

Once it’s completed, you can easily use these validators e.g. within your business logic services. It also comes in handy to configure them within the IoC container. For example using it with the Autofac could look like this:

var assembly = Assembly.GetAssembly(typeof(IValidator<>));
builder.RegisterAssemblyTypes(assembly).AsClosedTypesOf(typeof(IValidator<>));

And that would be all, it’s a relatively easy concept, yet also quite useful, mostly due to the fact that having a set of interfaces for validation purposes makes it easier for testing the code.





About List