Ninject 不会使用基于约定的绑定来注册 MediatR.IRequestHandler<,>?

Ninject just won't register MediatR.IRequestHandler<,> using convention-based binding?

按照以下示例:

MediatR.Examples.Ninject

我有一个 MediatorModule class 如下:

    public class MediatorModule : NinjectModule {
        public class ContravariantBindingResolver : NinjectComponent, IBindingResolver {
            public IEnumerable<IBinding> Resolve(Multimap<Type, IBinding> bindings, Type service) {
                if (service.IsGenericType) {
                    var genericType = service.GetGenericTypeDefinition();
                    var genericArguments = genericType.GetGenericArguments();

                    if (1 == genericArguments.Count() && genericArguments.Single().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Contravariant)) {
                        var argument = service.GetGenericArguments().Single();
                        var matches = bindings.Where(kvp => kvp.Key.IsGenericType
                                                         && kvp.Key.GetGenericTypeDefinition().Equals(genericType)
                                                         && kvp.Key.GetGenericArguments().Single() != argument
                                                         && kvp.Key.GetGenericArguments().Single().IsAssignableFrom(argument))
                                              .SelectMany(kvp => kvp.Value);
                        return matches;
                    }
                }

                return Enumerable.Empty<IBinding>();
            }
        }
        public override void Load() {
            Kernel.Components.Add<IBindingResolver, ContravariantBindingResolver>();
            Kernel.Bind(services => services.FromAssemblyContaining<IMediator>().SelectAllClasses().BindDefaultInterface());
            Kernel.Bind(services => services.FromThisAssembly().SelectAllClasses().InheritedFrom(typeof(IRequestHandler<,>)).BindAllInterfaces());
            Kernel.Bind(services => services.FromThisAssembly().SelectAllClasses().InheritedFrom(typeof(INotificationHandler<>)).BindAllInterfaces());
            Kernel.Bind(typeof(IPipelineBehavior<,>)).To(typeof(RequestPreProcessorBehavior<,>));
            Kernel.Bind(typeof(IPipelineBehavior<,>)).To(typeof(RequestPostProcessorBehavior<,>));
            Kernel.Bind(typeof(IPipelineBehavior<,>)).To(typeof(RequestExceptionActionProcessorBehavior<,>));
            Kernel.Bind(typeof(IPipelineBehavior<,>)).To(typeof(RequestExceptionProcessorBehavior<,>));
            Kernel.Bind<ServiceFactory>().ToMethod(ctx => t => ctx.Kernel.TryGet(t));
        }
    }

快速查看 services.FromThisAssembly().SelectAllClasses().InheritedFrom(typeof(IRequestHandler<,>)),我可以看到 class 已正确找到。

这是我的命令和处理程序的示例。

public class SupplierInvoice {
    public class ProcessCommand : IRequest<ProcessedTransaction> {
        public ProcessCommand(XmlDocument supplierInvoiceDocument)
            => SupplierInvoiceDocument = supplierInvoiceDocument;

        public XmlDocument SupplierInvoiceDocument { get; }
    }

    public class ProcessCommandHandler : IRequestHandler<ProcessCommand, ProcessedTransaction> {
        private readonly IXmlSerializer<SupplierInvoice> _serializer;
        private readonly IValidator<SupplierInvoice> _validator;
        private readonly IMediator _mediator;
        public ProcessCommandHandler(IXmlSerializer<SupplierInvoice> serializer, IValidator<SupplierInvoice> validator, IMediator mediator) {
            _serializer=serializer;
            _validator=validator;
            _mediator=mediator;
        }

        public async Task<ProcessedTransaction> Handle(ProcessCommand request, CancellationToken cancellationToken) {
            if (request == null) return ProcessedTransaction.Succeeded;

            var model = _serializer.Deserialize(request.SupplierInvoiceDocument.OuterXml);
            var vr = _validator.Validate(model);
            if (!vr.IsValid) // throwing exception with validation messages.

            return await _mediator.Send(new CreateCommand(model));
        }
    }
}

所以我想知道如何使用BindAllInterfaces方法不注册处理程序?

即使使用普通的旧绑定语法,请求处理程序也不会被注册。

Kernel.Bind<IRequestHandler<SupplierInvoice.ProcessCommand, ProcessedTransaction>>().To<SupplierInvoice.ProcessCommandHandler>();

我错过了什么?

根据链接 Ninject 示例:MediatR.Examples.Ninject, I guess I found an error in the ContravariantBindingResolver.cs class.

我猜错的那一行是在第二次获取通用参数时。

var genericType = service.GetGenericTypeDefinition();
var genericArguments = genericType.GetGenericArguments();
if (genericArguments.Count() == 1
   && genericArguments.Single().GenericParameterAttributes.HasFlag(GenericParameterAttributes.Contravariant))
{
    var argument = service.GetGenericArguments().Single();
    var matches = bindings.Where(kvp => kvp.Key.IsGenericType
                                   && kvp.Key.GetGenericTypeDefinition().Equals(genericType)
                                   && kvp.Key.GetGenericArguments().Single() != argument
                                   && kvp.Key.GetGenericArguments().Single().IsAssignableFrom(argument))
                        .SelectMany(kvp => kvp.Value);
   return matches;
}

请注意 var genericArguments = genericType.GetGenericArguments() 使用 service.GetGenericTypeDefinition() 方法调用返回的泛型类型定义。但是因为在提供的示例代码中第二次调用 GetGenericArguments 是从服务实例进行的,所以似乎没有充分返回正确的通用参数。因此,我建议使用已经声明的变量,其中包含可以从中获取参数变量值的通用参数。

总而言之,对我来说,让解析器每次都能真正解析正确的处理程序的原因是我更改了这一行:

var argument = service.GetGenericArguments().Single()

var argument = genericArguments.Single()

鉴于它是逆变的,因此只有可用的参数,因为已经对返回的参数数组的长度进行了检查。

为我更改此设置使未注册的处理程序异常与能够解析正确处理程序的工作代码之间存在差异。