jbogard / MediatR.Extensions.Microsoft.DependencyInjection

MediatR extensions for Microsoft.Extensions.DependencyInjection
MIT License
327 stars 90 forks source link

Added ability to register additional interface implementations #119

Closed BlackGad closed 2 years ago

BlackGad commented 2 years ago

Description

Internal DI discovery MediatR mechanism is black box for others. This PR adds ability to use same rules for additional custom types. Feature can be useful for validators and other behavior extensions.

Example

FluentValidation integration into pipeline without additional manual validators registration.

Behavior extension

Pre handle validation behavior

public class RequestValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    private readonly IEnumerable<IValidator<TRequest>> _validators;

    public RequestValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
    {
        _validators = validators;
    }

    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
    {
        foreach (var validator in _validators)
        {
            await validator.ValidateAndThrowAsync(request, cancellationToken);
        }

        return await next();
    }
}

DI registration

var provider = new ServiceCollection()
                .AddMediatR(config =>
                {
                    config.AdditionalClasses.Add(new TypeRegistration(typeof(IValidator<>)));
                }, typeof(Ping));
                .AddTransient(typeof(IPipelineBehavior<,>), typeof(RequestValidationBehavior<,>));
                .BuildServiceProvider();

Handler, Request and Validator implementation

public class CustomRequest : IRequest<bool>
{
    public string Title { get; set; }
}

public class CustomRequestValidator : AbstractValidator<CustomRequest>
{
    public CustomRequestValidator()
    {
        RuleFor(x => x.Title)
            .NotEmpty();
    }
}

public class CustomRequestHandler : IRequestHandler<CustomRequest, bool>
{
    public async Task<bool> Handle(CustomRequest request, CancellationToken cancellationToken)
    {
        await Task.Delay(0, cancellationToken);
        return true;
    }
}

Usage

var mediator = provider.GetRequiredService<IMediator>();
var result = await mediator.Send(new CustomRequest());
jbogard commented 2 years ago

The internal registration code is pulled from a 3rd-party container (StructureMap). If you want that registration behavior, you should Scrutor or a different container. I don't really want to expose/support registration code that I """borrowed""" from somewhere else.

BlackGad commented 2 years ago

The internal registration code is pulled from a 3rd-party container (StructureMap). If you want that registration behavior, you should Scrutor or a different container. I don't really want to expose/support registration code that I """borrowed""" from somewhere else.

But in fact everyone is use MediatR :) So source of this code is irrelevant now. You became too famous). Please, add ability to use custom interfaces. I have additional ideas for upgrade but this one is obvious for my projects.

Also MediatR is approved framework in my organization(I think it is common situation). I am not sure that new additional framework will be approved as well.

BlackGad commented 2 years ago

Also this will guaranty same DI registration behavior for all components.

BlackGad commented 2 years ago

Any chance to be merged?

jbogard commented 2 years ago

No, I really don't want to have to support this. I understand it's valuable but I don't want to get GitHub issues when this registration won't work for some other library for whatever reason.

BlackGad commented 2 years ago

Error that you afraid of will be the same for your interfaces. And time proofs that your algorithm is safe enough. kindly ask you to rethink your decision.

Alternative: @jbogard maybe then you will agree to encapsulate ConnectMultiOpenInterfaceToTypesClosing logic and make ServiceRegistrar.ConnectImplementationsToTypesClosing and ServiceRegistrar.ConnectMultiOpenInterfaceToTypesClosing public? But it is a backdoor :(