简体   繁体   English

用Simple Injector包装通用类型

[英]Wrapping generic types with Simple Injector

Problem 问题

I already have an Command handling framework and I'm trying to use Simple injector (3.3.2) to wrap my existing handlers in something Mediatr will understand. 我已经有一个Command处理框架,并且尝试使用简单注入器(3.3.2)将现有的处理程序包装成Mediatr可以理解的东西。 My commandhandlers always return a CommandResult so my handler interface only has TCommand as a Type variable whereas the handler interface Mediatr provides needs a TResult too. 我的命令处理程序始终返回CommandResult因此我的处理程序接口仅将TCommand作为Type变量,而Mediatr提供的处理程序接口也需要TResult

So I have a ICommandHandler<TCommand> and Mediatr needs as IRequestHandler<TRequest, TResult> 所以我有一个ICommandHandler<TCommand>和Mediatr需要作为IRequestHandler<TRequest, TResult>

Thought about 关于

I could change my ICommandHandler<TCommand> to also implement IRequestHandler<TCommand, CommandResult> but then I'd have to change ICommand<TCommand> to implement IRequest<TCommand, CommandResult> . 我可以将ICommandHandler<TCommand>更改为也实现IRequestHandler<TCommand, CommandResult>但是随后我必须更改ICommand<TCommand>来实现IRequest<TCommand, CommandResult> But I don't want to change existing code and have it coupled so tightly. 但是我不想更改现有代码并将其紧密耦合在一起。

I can intercept the ResolveUnregisteredType on SimpleInjector and return whatever Mediatr needs (this will work). 我可以在ResolveUnregisteredType上拦截ResolveUnregisteredType并返回Mediatr需要的任何东西(这将起作用)。 But then I need code that is dependent on My Code AND Mediatr AND SimpleInjector and I'd like to avoid that. 但是然后我需要依赖于我的代码 Mediatr SimpleInjector的 代码 我想避免这种情况。 If all else fails, this would be my fallback scenario. 如果所有其他方法都失败了,那将是我的后备方案。

Tried 试过了

I tried three ways to get the registrations to work, see the code 我尝试了三种方法来使注册生效,请参见代码

Code

It's a bit, at the top the tests where I expected at least one to pass. 在最高的测试中,我期望至少有一个测试能够通过。 Then the interfaces I have now and a TestCommand . 然后,我现在拥有的接口和一个TestCommand After that three regions with the stuff I tried. 在那三个区域之后,我尝试了。

BTW BTW

I'm not putting the Mediatr tag on this question because it could apply to any framework. 我没有将Mediatr标记放在这个问题上,因为它可以应用于任何框架。

using MediatR;
using NUnit.Framework;
using SimpleInjector;
using System;

namespace CommandHandlingTest
{
    public class Tests
    {
        [Test]
        public void Version_1()
        {
            var container = new Container();
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();

            container.Register(typeof(ICommandHandler<>), assemblies);
            container.Register(typeof(IRequestHandler<,>), assemblies);
            container.Verify();

            var commandBus = new MediatorCommandBus_1(container.GetInstance, container.GetAllInstances);

            var command = new TestCommand();

            Assert.DoesNotThrow(() => commandBus.Send(command));
            // Fails with Handler was not found for request of type CommandWrapped_1<TestCommand>
        }

        [Test]
        public void Version_2()
        {
            var container = new Container();
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();

            container.Register(typeof(ICommandHandler<>), assemblies);
            container.Register(typeof(IRequestHandler<,>), assemblies);
            container.Verify();

            var commandBus = new MediatorCommandBus_2(container.GetInstance, container.GetAllInstances);

            var command = new TestCommand();

            Assert.DoesNotThrow(() => commandBus.Send(command));
            // Fails with Handler was not found for request of type CommandWrapped_2<TestCommand, CommandResult>.
        }

        [Test]
        public void Version_3()
        {
            var container = new Container();
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();

            container.Register(typeof(ICommandHandler<>), assemblies);
            container.Register(typeof(IRequestHandler<,>), assemblies);
            container.Verify();

            var commandBus = new MediatorCommandBus_3(container.GetInstance, container.GetAllInstances);

            var command = new TestCommand();

            Assert.DoesNotThrow(() => commandBus.Send(command));
            // Fails with Handler was not found for request of type CommandWrapped_3<TestCommand, CommandResult>.
        }
    }

    /* Should not change */
    public interface ICommand { }


    /* Should not change */
    public interface ICommandBus
    {
        CommandResult Send<TCommand>(TCommand command) where TCommand : ICommand;
    }

    /* Should not change */
    public interface ICommandHandler<in TCommand>
        where TCommand : ICommand
    {
        CommandResult Handle(TCommand command);
    }

    /* Should not change */
    public class TestCommand : ICommand { }

    /* Should not change */
    public class TestHandler : ICommandHandler<TestCommand>
    {
        public CommandResult Handle(TestCommand command)
        {
            return new CommandResult { IsValid = true };
        }
    }

    /* Should not change */
    public class CommandResult
    {
        public bool IsValid { get; set; }
    }

    #region Version 1
    public class MediatorCommandBus_1 : ICommandBus
    {
        private readonly IMediator _mediator;

        public MediatorCommandBus_1(SingleInstanceFactory singleInstanceFactory, MultiInstanceFactory multiInstanceFactory)
        {
            _mediator = new Mediator(singleInstanceFactory, multiInstanceFactory);
        }

        public CommandResult Send<TCommand>(TCommand command)
            where TCommand : ICommand
        {
            return _mediator.Send(new CommandWrapped_1<TCommand>(command)).Result;
        }
    }

    public class WrappedHandler_1<TCommand, TResult, TWrappedCommand> :
        IRequestHandler<TWrappedCommand, TResult>
        where TCommand : ICommand
        where TWrappedCommand : CommandWrapped_1<TCommand>, IRequest<TResult>
        where TResult : CommandResult
    {
        private readonly ICommandHandler<TCommand> _commandHandler;

        public WrappedHandler_1(ICommandHandler<TCommand> commandHandler)
        {
            _commandHandler = commandHandler;
        }

        public TResult Handle(TWrappedCommand message)
        {
            var handle = _commandHandler.Handle(message.UnWrap());
            return handle as TResult;
        }
    }

    public class CommandWrapped_1<TCommand> : IRequest<CommandResult>
        where TCommand : ICommand
    {
        private readonly TCommand _command;

        public CommandWrapped_1(TCommand command)
        {
            _command = command;
        }

        public TCommand UnWrap() => _command;
    }
    #endregion

    #region Version 2
    public class MediatorCommandBus_2 : ICommandBus
    {
        private readonly IMediator _mediator;

        public MediatorCommandBus_2(SingleInstanceFactory singleInstanceFactory, MultiInstanceFactory multiInstanceFactory)
        {
            _mediator = new Mediator(singleInstanceFactory, multiInstanceFactory);
        }

        public CommandResult Send<TCommand>(TCommand command)
            where TCommand : ICommand
        {
            return _mediator.Send(new CommandWrapped_2<TCommand, CommandResult>(command)).Result;
        }
    }

    public class WrappedHandler_2<TCommand, TResult> :
        IRequestHandler<CommandWrapped_2<TCommand, TResult>, TResult>
        where TCommand : ICommand
        where TResult : CommandResult
    {
        private readonly ICommandHandler<TCommand> _commandHandler;

        public WrappedHandler_2(ICommandHandler<TCommand> commandHandler)
        {
            _commandHandler = commandHandler;
        }

        public TResult Handle(CommandWrapped_2<TCommand, TResult> message)
        {
            var handle = _commandHandler.Handle(message.UnWrap());
            return handle as TResult;
        }
    }

    public class CommandWrapped_2<TCommand, TResult> : IRequest<TResult>
        where TCommand : ICommand
        where TResult : CommandResult
    {
        private readonly TCommand _command;

        public CommandWrapped_2(TCommand command)
        {
            _command = command;
        }

        public TCommand UnWrap() => _command;
    }
    #endregion

    #region Version 3
    public class MediatorCommandBus_3 : ICommandBus
    {
        private readonly IMediator _mediator;

        public MediatorCommandBus_3(SingleInstanceFactory singleInstanceFactory, MultiInstanceFactory multiInstanceFactory)
        {
            _mediator = new Mediator(singleInstanceFactory, multiInstanceFactory);
        }

        public CommandResult Send<TCommand>(TCommand command)
            where TCommand : ICommand
        {
            return _mediator.Send(new CommandWrapped_3<TCommand, CommandResult>(command)).Result;
        }
    }

    public class WrappedHandler_3<TCommand, TResult> :
        IRequestHandler<ICommandWrapped_3<TCommand, TResult>, TResult>
        where TCommand : ICommand
        where TResult : CommandResult
    {
        private readonly ICommandHandler<TCommand> _commandHandler;

        public WrappedHandler_3(ICommandHandler<TCommand> commandHandler)
        {
            _commandHandler = commandHandler;
        }

        public TResult Handle(ICommandWrapped_3<TCommand, TResult> message)
        {
            var handle = _commandHandler.Handle(message.UnWrap());
            return handle as TResult;
        }
    }

    public class CommandWrapped_3<TCommand, TResult> : ICommandWrapped_3<TCommand, TResult>
        where TCommand : ICommand
        where TResult : CommandResult
    {
        private readonly TCommand _command;

        public CommandWrapped_3(TCommand command)
        {
            _command = command;
        }

        public TCommand UnWrap() => _command;
    }

    public interface ICommandWrapped_3<out TCommand, out TResult> : IRequest<TResult>
        where TCommand : ICommand
    {
        TCommand UnWrap();
    }
    #endregion
}

You should replace the followihng line: 您应该替换以下行:

container.Register(typeof(IRequestHandler<,>), assemblies);

With: 带有:

container.Register(typeof(IRequestHandler<,>), typeof(WrappedHandler_2<,>));

The batch-registration overload of Register that takes in a list of assemblies, skips generic registrations by default (unless you specify otherwise), since generic types often need special handling. 接收程序集列表的Register的批量注册重载,默认情况下会跳过通用注册(除非另行指定),因为通用类型通常需要特殊处理。 In your case you are actually not interested in batch-registering, since you only have one mapping that has your interest (which is the wrapped handler). 在您的情况下,您实际上对批量注册不感兴趣,因为只有一个您感兴趣的映射(这是包装的处理程序)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM