Overview of the FluentValidation library. Part 2. Collections

Validation rules for collection elements can be described in three ways:

  1. Method RuleForEach

  2. Combination of methods RuleForEach + ChildRules

  3. Combination of methods RuleFor + ForEach (the author of the library recommends using this method)

When applying the method RuleFor on a collection, subsequent validators specified in the chain will validate the collection, and not its elements, this is important.

Let's consider the first method, method RuleForEach:

// Модель клиента
public class Customer
{
  // Адреса
  public List<string>? Addresses { get; set; }
}

// Валидатор для модели клиента
public class CustomerValidator : AbstractValidator<Customer>
{
  public CustomerValidator()
  {
    // Описываем правило, которое говорит о том, что каждый элемент
    // коллекции Addresses должен быть не равен null
    RuleForEach(customer => customer.Addresses)
        .NotNull();
  }
}

The code above will call the property validator NotNull for each element of the collection.

You can also combine the method RuleForEach with method SetValidatorwhen the collection contains complex objects (classes, structures, etc.):

// Модель заказа
public class Order
{
  // Общая стоимость
  public decimal TotalCost { get; set; }
}

// Валидатор для модели заказа
public class OrderValidator : AbstractValidator<Order>
{
  public OrderValidator()
  {
    // Метод GreaterThan говорит о том, что значение свойства TotalCost
    // должно быть больше 0
    RuleFor(order => order.TotalCost).GreaterThan(0);
  }
}

// Модель клиента
public class Customer
{
  // Заказы
  public List<Order>? Orders { get; set; }
}

// Модель валидатора для модели клиента
public class CustomerValidator : AbstractValidator<Customer>
{
  public CustomerValidator()
  {
    // Переиспользуем валидатор OrderValidator на каждый элемент
    // коллекции Orders через вызов метода SetValidator
    RuleForEach(customer => customer.Orders)
        .SetValidator(new OrderValidator());
  }
}

Optionally, you can filter collection elements using the method Where, which will be subject to validation. Method Where must be directly called after the method RuleForEach:

// Модель заказа
public class Order
{
  // Теперь поле имеет тип decimal?
  public decimal? TotalCost { get; set; }
  // Другие свойства...
}

public class CustomerValidator : AbstractValidator<Customer>
{
  public CustomerValidator()
  {
    // Фильтрация элементов коллекции через метод Where для последующей их валидации.
    // Валидируются только те элементы, у которых значение свойства TotalCost
    // не равно null
    RuleForEach(customer => customer.Orders)
        .Where(order => order.TotalCost is not null)
        .SetValidator(new OrderValidator());
  }
}

Let's consider the second method, a combination of methods RuleForEach + ChildRules:

As an alternative to combination RuleForEach + SetValidator you can use a combination RuleForEach + ChildRules. ChildRules look like SetValidatorbut differs in that validations are described not in a separate validator, but directly in the same validator (inline validator):

// Валидатор для модели клиента
public class CustomerValidator : AbstractValidator<Customer>
{
  public CustomerValidator()
  {
    RuleForEach(customer => customer.Orders)
      .ChildRules(validator =>
      {
        // Метод GreaterThan говорит о том, что значение свойства TotalCost
        // должно быть больше 0
        validator.RuleFor(order => order.TotalCost).GreaterThan(0);
      });
  }
}

Last third method, combination RuleFor + ForEach:

// Валидатор для модели клиента
public class CustomerValidator : AbstractValidator<Customer>
{
  public CustomerValidator()
  {
    // Коллекция Orders должна быть не равна null
    RuleFor(customer => customer.Orders)
      .NotNull()
      .ForEach(rule =>
      {
          // Значение свойства TotalCost должно быть
          // больше 0 (у каждого элемента коллекции Orders)
          rule.Must(order => order.TotalCost > 0);
      });
  }
}

Method Must this is a universal validator that allows you to specify any custom predicate (which was not included out of the box), in this case it requires that the property value TotalCost there were more 0for successful validation.

The author recommends using this method out of the three because it is cleaner, easier to read, and more flexible. It is possible to specify validations simultaneously for both the collection and its elements.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *