【问题标题】:Wrapping generic types with Simple Injector使用 Simple Injector 包装泛型类型
【发布时间】:2017-01-15 21:03:54
【问题描述】:

问题

我已经有一个命令处理框架,我正在尝试使用简单注入器 (3.3.2) 将我现有的处理程序包装在 Mediatr 可以理解的东西中。我的命令处理程序总是返回一个CommandResult,所以我的处理程序接口只有TCommand 作为类型变量,而 Mediatr 提供的处理程序接口也需要一个TResult

所以我有一个ICommandHandler<TCommand> 和 Mediatr 需要 IRequestHandler<TRequest, TResult>

想过

我可以将ICommandHandler<TCommand> 更改为也实现IRequestHandler<TCommand, CommandResult>,但随后我必须更改ICommand<TCommand> 以实现IRequest<TCommand, CommandResult>。但我不想更改现有代码并将其耦合得如此紧密。

我可以拦截 SimpleInjector 上的 ResolveUnregisteredType 并返回 Mediatr 需要的任何内容(这将起作用)。但是我需要依赖于 My Code AND Mediatr AND SimpleInjector 的代码我想避免这种情况。 如果一切都失败了,这将是我的后备方案。

试过

我尝试了三种方法来让注册工作,查看代码

代码

有点,在顶部的测试中,我希望至少有一个通过。然后是我现在拥有的接口和TestCommand。在这三个区域之后,我尝试了这些东西。

顺便说一句

我没有在这个问题上加上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
}

【问题讨论】:

  • 问题中缺少的是解释您实际使用 MediatR 的原因。您已经有了处理命令的特定于应用程序的抽象;还有外部(不兼容)接口有什么用?
  • 好问题。我拥有的抽象是对我有用的最简单的。 MediatR 是一个广泛使用的库,所以我选择首先使用它。我不确定最终解决方案中是否会使用其他选项,但其他实现可能会出现类似的问题。
  • 如果我理解正确,您在此处遇到的实际 only 问题是从 MediatR 映射到您自己的界面并返回。从等式中删除 MediatR 即可解决问题。
  • 是的,但我必须用另一个实现来替换它。我不想自己写。也许我会使用远程总线或进程间通信。无论哪种方式,问题可能都是一样的。我也很好奇如何解决这个问题以及为什么它不起作用。
  • MediatR 为您解决了什么问题?

标签: c# dependency-injection simple-injector


【解决方案1】:

您应该替换以下行:

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

与:

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

Register 的批处理注册重载接收程序集列表,默认情况下会跳过泛型注册(除非您另外指定),因为泛型类型通常需要特殊处理。在您的情况下,您实际上对批量注册不感兴趣,因为您只有一个您感兴趣的映射(即包装的处理程序)。

【讨论】:

    猜你喜欢
    • 2018-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-10
    • 2014-11-23
    • 1970-01-01
    相关资源
    最近更新 更多