Обзор библиотеки FluentValidation. Часть 6. Внедрение зависимостей

83b076787d1e394f96cfccb1dbed8877.png

Библиотеку FluentValidation можно использовать с любой библиотекой внедрения зависимостей. В этой части будут примеры на библиотеке Microsoft.Extensions.DependencyInjection. У нас есть следующие валидаторы:

// Модель адреса
public class Address { ... }
// Валидатор для модели адреса
public class AddressValidator : AbstractValidator
{ ... } // Модель заказа public class Order { ... } // Валидатор для модели заказа public class OrderValidator : AbstractValidator { ... } // Модель клиента public class Customer { // Имя public string? Forename { get; set; } // Адрес public Address? Address { get; set; } } // Валидатор для модели клиента public class CustomerValidator : AbstractValidator { // Получаем через конструктор валиадтор для модели адреса (AddressValidator) public CustomerValidator(IValidator
addressValidator) { RuleFor(customer => customer.Forename) .NotNull(); // Применяем полученный валидатор RuleFor(customer => customer.Address) .SetValidator(addressValidator); } }

И следующий сервис:

// Сервис клиента
public class CustomerService
{
  private readonly IValidator _validator;

  // Получаем CustomerValidator через конструктор
  public CustomerService(IValidator validator)
  {
    _validator = validator;    
  }

  // Валидируем, возвращаем результат валидации (true/false)
  public bool Validate(Customer customer)
  {
    var result = _validator.Validate(customer);
    return result.IsValid;
  }
}

Ручная регистрация.

Валидаторы можно регистрировать через интерфейс IValidator где T тип валидируемой модели:

using Microsoft.Extensions.DependencyInjection;

static void Main(string[] args)
{
  var services = new ServiceCollection();

  services.AddScoped();
  // Регистрируем валидаторы
  services.AddScoped, CustomerValidator>();
  services.AddScoped, AddressValidator>();
  // AddScoped можно заменить на AddTransient, AddSingleton по необходимости

  var provider = services.BuildServiceProvider();

  // Создаём модель клиента
  var customer = new Customer { };
  // Получаем CustomerService из DI контейнера
  var customerService = provider.GetRequiredService();
  // Валидируем модель клиента, получаем результат валидации
  var isValid = customerService.Validate(customer);
}

Автоматическая регистрация.

Для автоматической регистрации валидаторов, нужно «подтянуть» пакет FluentValidation.DependencyInjectionExtensions. В нём предусмотрено 4 публичных метода расширения:

Метод расширения: AddValidatorsFromAssemblyContaining
Описание: Зарегистрировать все валидаторы, которые есть в сборке T типа:

static void Main(string[] args)
{
  var services = new ServiceCollection();

  // Регистрируем все валидаторы, которые есть в сборке типа CustomerValidator
  // Указываем через параметры типа
  services.AddValidatorsFromAssemblyContaining();

  var provider = services.BuildServiceProvider();
  // ...
}

Метод расширения: AddValidatorsFromAssemblyContaining(Type)
Описание:
Зарегистрировать все валидаторы, которые есть в сборке Type типа:

static void Main(string[] args)
{
  var services = new ServiceCollection();

  // Регистрируем все валидаторы, которые есть в сборке типа CustomerValidator
  // Работает аналогично примеру выше, только указываем через объект типа Type, 
  // а не через параметры типа
  services.AddValidatorsFromAssemblyContaining(typeof(CustomerValidator));

  var provider = services.BuildServiceProvider();
  // ...
}

Метод расширения: AddValidatorsFromAssembly(Assembly)
Описание:
Зарегистировать все валидаторы, которые есть в указанной сборке Assembly:

static void Main(string[] args)
{
  var services = new ServiceCollection();

  // Регистрируем все валидаторы, которые есть в сборке
  // под названием FluentValidationTests 
  services.AddValidatorsFromAssembly(Assembly.Load("FluentValidationTests"));

  var provider = services.BuildServiceProvider();
  // ...
}

Метод расширения: AddValidatorsFromAssemblies(IEnumerable
Описание: Зарегистрировать все валидаторы, которые есть в указанных сборках Assembly:

static void Main(string[] args)
{
  var services = new ServiceCollection();

  var assemblies = new[]
  {
    "FluentValidationTests",
    "SomeAnotherAssembly"
  }.Select(Assembly.Load);

  // Регистрируем все валидаторы, которые есть в сборках под названиями:
  // FluentValidationTests и SomeAnotherAssembly
  services.AddValidatorsFromAssemblies(assemblies);

  var provider = services.BuildServiceProvider();
  // ...
}

Жизненный цикл валидаторов при автоматической регистрации.

Вы можете опционально настраивать жизненный цикл валидаторов, с помощью параметра lifetime и значения перечисления ServiceLifetime (по умолчанию указано ServiceLifetime.Scoped):

static void Main(string[] args)
{
  var services = new ServiceCollection();

  // Указываем для всех валидаторов жизненный цикл Scoped (новый объект на указанную область)
  services.AddValidatorsFromAssemblyContaining(lifetime: ServiceLifetime.Scoped);

  var provider = services.BuildServiceProvider();
  // ...
}

Фильтрация валидаторов при автоматической регистрации.

Вы можете опционально указать предикат, по которому будет осуществляться проверка, стоит ли регистрировать очередной валидатор в DI контейнер или же нет. Код из примера ниже показывает, как исключить валидаторы AddressValidator и OrderValidator, при этом зарегистировав все остальные:

static void Main(string[] args)
{
  var services = new ServiceCollection();

  var excludeValidators = new[]
  {
      typeof(AddressValidator),
      typeof(OrderValidator),
  };
  // Все валидаторы будут зарегистрированы, кроме тех, которые есть в массиве excludeValidators
  services.AddValidatorsFromAssemblyContaining(filter: filter => !excludeValidators.Contains(filter.ValidatorType));

  var provider = services.BuildServiceProvider();
  // ...
}

Такой же пример, только исключаем через интерфейс IValidator:

// ...

public class OrderValidator : AbstractValidator { ... }

static void Main(string[] args)
{
  var services = new ServiceCollection();

  var excludeValidators = new[]
  {
      typeof(IValidator
), typeof(IValidator), }; // Все валидаторы будут зарегистрированы, кроме тех, которые есть в массиве excludeValidators services.AddValidatorsFromAssemblyContaining(filter: filter => !excludeValidators.Contains(filter.InterfaceType)); var provider = services.BuildServiceProvider(); // ... }

Регистрация internal валидаторов при автоматической регистрации.

Вы можете опционально указать, стоит ли регистрировать валидаторы, у которых указан модификатор доступа internal, через параметр includeInternalTypes:

static void Main(string[] args)
{
  var services = new ServiceCollection();

  // Регистрируем все валидаторы, включая определённые с модификатором доступа
  // internal
  services.AddValidatorsFromAssemblyContaining(includeInternalTypes: true);

  var provider = services.BuildServiceProvider();
  // ...
}

А есть ли такие же параметры у других методов расширений?

Да, есть. У каждого вышеперечисленного метода расширения есть опциональные параметры lifetime, filter, includeInternalTypes:

lifetime по умолчанию равен ServiceLifetime.Scoped

filter по умолчанию равен null

includeInternalTypes по умолчанию равен false

← Предыдущая часть

© Habrahabr.ru