c # Simple Injector Inject Decorator в классе только от одного клиента - PullRequest
0 голосов
/ 28 мая 2019

Hy, ребята,
У меня проблема, у меня есть такой интерфейс:

public interface ICommand<in TRequest, out TResponse> 
    where TRequest : class
    where TResponse : BaseResponse
{
    TResponse Execute(TRequest request);
}

тогда я два класса, который реализует этот интерфейс следующим образом:

public class ExternalAddUser : ICommand<ExternalAddUserRequest, ExternalAddUserResponse>
{
    private readonly ICommand<AddUserRequest, AddUserResponse> _command;

    public ExternalAddUser(ICommand<AddUserRequest, AddUserResponse> command)
    {
        _command = command;
    }

    public ExternalAddUserResponse Execute(ExternalAddUserRequest request)
    {
        var response = _command.Execute(Mapper.Map<AddUserRequest>(request));

        return Mapper.Map<ExternalAddUserResponse>(response);
    }
}

и это:

public class AddUser : ICommand<AddUserRequest, AddUserResponse>
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly IMessageService _messageService;
    private readonly IDefaultSettings _settings;
    private readonly IMessageFactory _messageFactory;

    public AddUser(IUnitOfWork unitOfWork, IMessageService messageService, IDefaultSettings settings, IMessageFactory messageFactory)
    {
        _unitOfWork = unitOfWork;
        _messageService = messageService;
        _settings = settings;
        _messageFactory = messageFactory;
    }

    public AddUserResponse Execute(AddUserRequest request)
    {
        // My implementation here
    }
}

Интерфейс IMessageFactory - это шаблон «Фабрика / Шаблон», который создает интерфейс IMessage только с такими свойствами, как: Body, Subject, Language. Я зарегистрировал свой класс с простым инжектором, как это:

container.Register(typeof(ICommand<,>), businessLayerAssembly);   
container.Register<IDefaultSettings, DefaultSettings>(Lifestyle.Singleton);
container.Register<ISecuritySettings, SecuritySettings>(Lifestyle.Singleton);
container.RegisterConditional<IMessageFactory, ActivationMessageFactory>
            (c => c.Consumer.ImplementationType == typeof(AddUser) 
                  || c.Consumer.ImplementationType == typeof(SendActivationEmail));
container.RegisterConditional<IMessageFactory, RecoveryMessageFactory>
            (c => !c.Handled);

Теперь у меня есть другой класс, который является Decorator из ActivationMessageFactory, например:

public class ActivationMessageWithoutLinkFactory : IMessageFactory 
{
    private readonly IMessageFactory _messageFactory;

    public ActivationMessageWithoutLinkFactory(IMessageFactory messageFactory)
    {
         _messageFactory = messageFactory;
    }

    public IMessage CreateMessage(MessageData messageData)
    {
        // Implementation
    }
}

Мой вопрос:
Можно ли добавить ActivationMessageWithoutLinkFactory decorator в класс AddUser, когда этот класс вызывается из класса ExternalAddUser? Пример кода запаха:

public class ExternalAddUser : ICommand<ExternalAddUserRequest, ExternalAddUserResponse>
{
    public ExternalAddUserResponse Execute(ExternalAddUserRequest request)
    {

        ICommand<AddUserRequest, AddUserResponse> command = new AddUser(new SqlUnitOfWork(), new EmailService(), 
            new DefaultSettings(), new ActivationMessageWithoutLinkFactory(new ActivationMessageFactory()));
    }
}

Это граф объектов, который я хочу построить:

// AddUser injected into ExternalAddUser
new ExternalAddUser(
    new AddUser(
        new UnitOfWork(),
        new MessageService(),
        new DefaultSettings(),
        new ActivationMessageWithoutLinkFactory(
            new ActivationMessageFactory())))

// AddUser injected into anything else
new AnythingElse(
    new AddUser(
        new UnitOfWork(),
        new MessageService(),
        new DefaultSettings(),
        new ActivationMessageFactory())) // no decorator

Спасибо за ваш ответ, надеюсь, я был ясен.

1 Ответ

1 голос
/ 05 июня 2019

То, что вы пытаетесь достичь, это применить декоратор на основе потребителя потребителя. Это не может быть легко достигнуто в Простом Инжекторе. Вместо этого вы можете попробовать следующее: вместо того, чтобы делать декоратор условным, вставьте в него контекстные данные, которые могут быть установлены в начале запроса. Таким образом, декоратор может решить, должна ли быть выполнена его логика, или он должен просто переадресовать вызов своему декретируемому.

UPDATE

Вы можете определить следующую абстракцию:

public class ICommandContext
{
    Type RootRequest { get; }
}

Эта абстракция позволяет вам проверить тип корневого запроса, который в данный момент выполняется. Вы можете использовать эту абстракцию внутри вашего декоратора:

public class ActivationMessageWithoutLinkFactory : IMessageFactory 
{
    private readonly ICommandContext _context;
    private readonly IMessageFactory _messageFactory;

    public ActivationMessageWithoutLinkFactory(
        ICommandContext context,
        IMessageFactory messageFactory)
    {
        _context = context;
        _messageFactory = messageFactory;
    }

    public IMessage CreateMessage(MessageData messageData)
    {
        if (_context.RootRequest == typeof(ExternalAddUser))
        {
            // Begin decorated stuff
            var message = _messageFactory.CreateMessage(messageData);
            // End decorated stuff

            return message;
        }
        else
        {
            return _messageFactory.CreateMessage(messageData);
        }
    }
}

Внутри вашего Composition Root теперь вы можете создать ICommandContext реализацию и ICommand<,> декоратор, который может управлять этим контекстом:

public class CommandContext : ICommandContext
{
    public Stack<Type> Requests = new Stack<Type>();

    public Type RootRequest => Requests.First();
}

public class ContextCommandDecorator<TRequest, TResponse> : ICommand<TRequest, TResponse>
{
    private readonly CommandContext _context;
    private readonly ICommand<TRequest, TResponse> _decoratee;
    public ContextCommandDecorator(
        CommandContext context,
        ICommand<TRequest, TResponse> decoratee)
    {
        _context = context;
        _decoratee = decoratee;
    }

    public TResponse Execute(TRequest request)
    {
        _context.Push(typeof(TRequest));

        try
        {
            return _decoratee.Execute(request);
        }
        finally
        {
            _context.Pop();
        }
    }
}

Наконец, вы можете добавить следующие три регистрации в ваше приложение:

container.Register<ICommandContext, CommandContext>(Lifestyle.Scoped);
container.Register<CommandContext>(Lifestyle.Scoped);

container.RegisterDecorator(typeof(ICommand<,>), typeof(ContextCommandDecorator<,>));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...