Pull to refresh

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

Level of difficultyEasy
Reading time5 min
Views2.4K

← Предыдущая часть | Следующая часть →

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

// Модель адреса
public class Address { ... }
// Валидатор для модели адреса
public class AddressValidator : AbstractValidator<Address> { ... }

// Модель заказа
public class Order { ... }
// Валидатор для модели заказа
public class OrderValidator : AbstractValidator<Order> { ... }

// Модель клиента
public class Customer
{
  // Имя
  public string? Forename { get; set; }
  // Адрес
  public Address? Address { get; set; }
}

// Валидатор для модели клиента
public class CustomerValidator : AbstractValidator<Customer>
{
  // Получаем через конструктор валиадтор для модели адреса (AddressValidator)
  public CustomerValidator(IValidator<Address> addressValidator)
  {
    RuleFor(customer => customer.Forename)
      .NotNull();

    // Применяем полученный валидатор
    RuleFor(customer => customer.Address)
      .SetValidator(addressValidator);
  }
}

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

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

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

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

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

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

using Microsoft.Extensions.DependencyInjection;

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

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

  var provider = services.BuildServiceProvider();

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

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

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

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

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

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

  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>)
Описание: Зарегистрировать все валидаторы, которые есть в указанных сборках 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<CustomerValidator>(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<CustomerValidator>(filter: filter => !excludeValidators.Contains(filter.ValidatorType));

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

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

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

  var excludeValidators = new[]
  {
      typeof(IValidator<Address>),
      typeof(IValidator<Order>),
  };
  // Все валидаторы будут зарегистрированы, кроме тех, которые есть в массиве excludeValidators
  services.AddValidatorsFromAssemblyContaining<CustomerValidator>(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<CustomerValidator>(includeInternalTypes: true);

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

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

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

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

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

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

← Предыдущая часть | Следующая часть →

Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
Total votes 6: ↑2 and ↓40
Comments0

Articles