Reputation: 894
Hy guys,
I have a problem, I have an interface like this:
public interface ICommand<in TRequest, out TResponse>
where TRequest : class
where TResponse : BaseResponse
{
TResponse Execute(TRequest request);
}
then I two class that implements this interface like this:
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);
}
}
and this:
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
}
}
The Interface IMessageFactory is a "Factory/Template" Pattern that creates an IMessage interface with only property like: Body, Subject, Language. I have registered my class with simple injector like this:
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);
Now I have another class that is Decorator of ActivationMessageFactory like this:
public class ActivationMessageWithoutLinkFactory : IMessageFactory
{
private readonly IMessageFactory _messageFactory;
public ActivationMessageWithoutLinkFactory(IMessageFactory messageFactory)
{
_messageFactory = messageFactory;
}
public IMessage CreateMessage(MessageData messageData)
{
// Implementation
}
}
My question is:
Is possible to inject ActivationMessageWithoutLinkFactory decorator in AddUser class when this class is called from ExternalAddUser class?
Smell code example:
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()));
}
}
This is the object graph I wish to construct:
// 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
Thank you for you answer, I hope i was clear.
Upvotes: 0
Views: 219
Reputation: 172816
What you are trying to achieve is to apply a decorator based on the consumer's consumer. This can't be easily achieved in Simple Injector. Instead, you can try the following: Instead of making the decorator conditional, inject it with contextual data that can be set by the start of the request. This way the decorator can decide whether or not its logic should be executed, or it should simply forward the call to its decoratee.
UPDATE:
You can define the following abstraction:
public class ICommandContext
{
Type RootRequest { get; }
}
This abstraction allows you to check what the type is of the root request that is currently running. You can use this abstraction inside your decorator:
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);
}
}
}
Inside your Composition Root, you can now create an ICommandContext
implementation and a ICommand<,>
decorator that can manage this context:
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();
}
}
}
Finally, you can add the following three registrations to your application:
container.Register<ICommandContext, CommandContext>(Lifestyle.Scoped);
container.Register<CommandContext>(Lifestyle.Scoped);
container.RegisterDecorator(typeof(ICommand<,>), typeof(ContextCommandDecorator<,>));
Upvotes: 1