注册Mediatr IPipelineBehavior的多个实现时,了解Asp.net核心依赖注入

时间:2020-04-22 06:14:01

标签: c# asp.net-core .net-core dependency-injection mediatr

我看到了使用Asp.Net Core 3.1和Mediatr(使用IPipelineBehavior)的以下代码。

Startup

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的更多文档?

更新

感谢Deepak MishraSteven帮助我理解。我了解到,这就是dotnet DI解决同一接口的多种实现的方式。要获得所有实现,我将使用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 个答案:

答案 0 :(得分:1)

注册同一接口的多个实现时,DI可以使用包含所有实现的IEnumerable进行解析。如果未指定IEnumerable,则它将仅包含第一个实现。

相关问题