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

от автора

Библиотеку 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

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


ссылка на оригинал статьи https://habr.com/ru/articles/799493/