Я видел следующий код, используя Asp. Net Core 3.1 с Mediatr, используя IPipelineBehavior.
Запуск
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestPerformanceBehaviour<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestValidationBehavior<,>));
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(UnhandledExceptionBehaviour<,>));
RequestValidationBehavior
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using FluentValidation;
using MediatR;
using ValidationException = CleanArchitecture.Application.Common.Exceptions.ValidationException;
public class RequestValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public RequestValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
if (_validators.Any())
{
var context = new ValidationContext(request);
var failures = _validators
.Select(v => v.Validate(context))
.SelectMany(result => result.Errors)
.Where(f => f != null)
.ToList();
if (failures.Count != 0)
{
throw new ValidationException(failures);
}
}
return next();
}
}
Однако, исходя из моего ограниченного понимания asp. net DI, не должен ли _validators
быть типа IValidator<TRequest>
? Почему это типа IEnumerable<IValidator<TRequest>>
?
При выполнении кода _validators
всегда имеет длину = 1.
Где можно найти дополнительную документацию о том, когда DI разрешит реализацию в IEnumerable?
Обновление
Спасибо Дипак Мишре и Стивену за помощь в понимании. Я узнал, что именно так tnet D разрешает несколько реализаций одного и того же интерфейса. Чтобы получить все реализации, я бы использовал IEnumerable<Interface>
, чтобы получить все сервисы, которые его реализуют. Вот рабочий пример, демонстрирующий это:
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
namespace DotnetCoreDependencyInjection
{
class Program
{
static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
services.AddSingleton<IValidator<Shipment>, ValidateSourceAddress>();
services.AddSingleton<IValidator<Shipment>, ValidateDestinationAddress>();
var serviceProvider = services.BuildServiceProvider();
using var scope = serviceProvider.CreateScope();
// DI will resolve IEnumerable<IValidator<Shipment>> to all implementations of IValidator<Shipment>
var validators = scope.ServiceProvider.GetService<IEnumerable<IValidator<Shipment>>>();
foreach (var validator in validators)
{
validator.Validate(new Shipment{ SourceAddress = "Source Address", DestinationAddress = "Destination Address"});
}
}
}
class Shipment
{
public int Id { get; set; }
public string DestinationAddress { get; set; }
public string SourceAddress { get; set; }
}
interface IValidator<T>
{
void Validate(T shipment);
}
class ValidateSourceAddress : IValidator<Shipment>
{
public void Validate(Shipment shipment)
{
Console.WriteLine($"Validate SourceAddress: {shipment.SourceAddress}");
}
}
class ValidateDestinationAddress : IValidator<Shipment>
{
public void Validate(Shipment shipment)
{
Console.WriteLine($"Validate DestinationAddress: {shipment.DestinationAddress}");
}
}
}