Понимание Asp. net Внедрение основных зависимостей при регистрации нескольких реализаций Mediatr IPipelineBehavior - PullRequest
0 голосов
/ 22 апреля 2020

Я видел следующий код, используя 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}");
        }
    }
}

1 Ответ

1 голос
/ 22 апреля 2020

Когда вы регистрируете несколько реализаций одного и того же интерфейса, DI может разрешаться с IEnumerable, содержащим всю реализацию. Если вы не укажете IEnumerable, он будет содержать только первую реализацию.

...