Обтекание универсальных типов с помощью простого инжектора

проблема

У меня уже есть среда обработки команд, и я пытаюсь использовать простой инжектор (3.3.2), чтобы обернуть мои существующие обработчики в то, что Mediatr поймет. Мои командные обработчики всегда возвращают CommandResult так что мой интерфейс обработчика имеет только TCommand в качестве переменной типа, тогда как интерфейс обработчика, который обеспечивает Mediatr, требует TResult тоже.

Итак, у меня есть ICommandHandler<TCommand> а Медиатр нужен как IRequestHandler<TRequest, TResult>

Думал о

Я мог бы изменить свой ICommandHandler<TCommand> также реализовать IRequestHandler<TCommand, CommandResult> но тогда я должен был бы изменить ICommand<TCommand> реализовать IRequest<TCommand, CommandResult>, Но я не хочу менять существующий код и связывать его так тесно.

Я могу перехватить ResolveUnregisteredType на SimpleInjector и вернуть все, что нужно Mediatr (это будет работать). Но тогда мне нужен код, который зависит от моего кода, Mediatr и 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
}

1 ответ

Решение

Вам следует заменить следующую строку:

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

С:

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

Перегрузка партии регистрации Register он принимает список сборок, по умолчанию пропускает общие регистрации (если не указано иное), поскольку общие типы часто требуют специальной обработки. В вашем случае вы на самом деле не заинтересованы в пакетной регистрации, поскольку у вас есть только одно сопоставление, которое вас интересует (это обработчик с упаковкой).

Другие вопросы по тегам