c# Simple Injector 仅从一个客户端在 class 中注入装饰器

c# Simple Injector inject decorator in a class only from one client

伙计们,
我有一个问题,我有一个这样的界面:

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

然后我两个 class 实现这个接口是这样的:

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 是一个 "Factory/Template" 模式,它创建一个只有 属性 的 IMessage 接口,例如:正文、主题、语言。 我已经用这样的简单注射器注册了我的 class:

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);

现在我有另一个 class 是 ActivationMessageFactory 的装饰器,如下所示:

public class ActivationMessageWithoutLinkFactory : IMessageFactory 
{
    private readonly IMessageFactory _messageFactory;

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

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

我的问题是:
当从 ExternalAddUser class 调用此 class 时,是否可以在 AddUser class 中注入 ActivationMessageWithoutLinkFactory 装饰器? 气味代码示例:

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

谢谢你的回答,我希望我已经清楚了。

您要实现的是基于消费者的消费者应用装饰器。这在 Simple Injector 中不容易实现。相反,您可以尝试以下操作:不是使装饰器有条件,而是将其注入可在请求开始时设置的上下文数据。这样装饰者可以决定是否应该执行其逻辑,或者它应该简单地将调用转发给它的装饰者。

更新:

你可以定义如下抽象:

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<,>));