If you register all command handlers by their ICommandHandler<TCommand, TResult>
interface, you can't wrap part of the handlers with a decorator that wraps an ITransactionalCommandHandler<TCommand, TResult>
. You can only apply such decorator if the handlers are explicitly registered by that ITransactionalCommandHandler<TCommand, TResult>
interface, but this is not something you should do. You don't want that, since that means that the consumers of those commands need to know whether the handlers is transactional or not, which they shouldn't care about (it's a implementation detail).
Your transactional decorator should therefore wrap an ICommandHandler<TCommand, TResult>
. If it needs access to the ITransactionalCommandHandler<TCommand, TResult>
interface, the decorator should cast the input parameter from ICommandHandler
to ITransactionalCommandHandler
.
What you need is to register your decorators based on a predicate, as follows:
// NOTE: In Simple Injector v2.x use 'ManyForOpenGeneric' instead.
container.Register(typeof(ICommandHandler<,>), myAssemblies);
container.RegisterDecorator(typeof(ICommandHandler<,>), typeof(TransactionDecorator<,>),
c => typeof(ITransactionalCommandHandler<,>)
.MakeGenericType(c.ServiceType.GetGenericArguments())
.IsAssignableFrom(c.ImplementationType));
container.RegisterDecorator(typeof(ICommandHandler<,>), typeof(AuthorisationDecorator<,>));
The predicate ensures that only command handlers are decorated that implement the ITransactionalCommandHandler<,>
.
Instead of using that interface, you might also mark the handlers with an attribute:
[Transactional]
class ShipOrderCommandHandler : ICommandHandler<ShipOrderCommand, Unit> { ... }
In that case the registration would become:
container.RegisterDecorator(typeof(ICommandHandler<,>), typeof(TransactionDecorator<,>),
c => c.ImplementationType.GetCustomAttribute<TransactionalAttribute>() != null);
Yet another option is to mark the command itself with an interface or inherit from a base class:
public class ShipOrderCommand : ICommand<Unit>, ITransactionalCommand
{
public Guid OrderId;
}
This allows you to add a generic type constraint on the TransactionDecorator<,>
and allows you to remove the predicate on the RegisterDecorator
registration:
public class TransactionDecorator<int TCommand, out TResult>
: ICommandHandler<TCommand, TResult>
where TCommand : ITransactionalCommand
{
// etc
}
Since Simple Injector understands type constraints, you can simplify the registration as follows:
container.RegisterDecorator(typeof(ICommandHandler<,>), typeof(TransactionDecorator<,>));
Do note that I don't advice this last approach in this particular case, since implementing an ITransactionalCommand
interface on the command again means that implementation details are leaking into the definition of the command. The command and its consumers don't need to know about whether transactions are needed on the command handler (and this is something that might change in the future).