Hi I am starting to use Simple Injector DI container for all my projects and would like some advice on how to fit is powerful features to my requirements.
I have a couple of command handler decorators which will wrap commands:
public class TransactionCommandHandlerDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> handlerToCall;
private readonly IUnitOfWork unitOfWork;
public TransactionCommandHandlerDecorator(
IUnitOfWork unitOfWork,
ICommandHandler<TCommand> decorated)
{
this.handlerToCall = decorated;
this.unitOfWork = unitOfWork;
}
public void Handle(TCommand command)
{
this.handlerToCall.Handle(command);
unitOfWork.Save();
}
}
Mutex decorator:
public class TransactionCommandHandlerWithMutexDecorator<TCommand>
: ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> handlerToCall;
private readonly IUnitOfWork unitOfWork;
private static object Lock = new object();
public TransactionCommandHandlerWithMutexDecorator(
IUnitOfWork unitOfWork,
ICommandHandler<TCommand> decorated)
{
this.handlerToCall = decorated;
this.unitOfWork = unitOfWork;
}
public void Handle(TCommand command)
{
lock (Lock)
{
this.handlerToCall.Handle(command);
unitOfWork.Save();
}
}
}
In some cases it only makes sense to wrap some commands in this blocking way (by using TransactionCommandHandlerWithMutexDecorator), and allow others unhinged access across all threads (with TransactionCommandHandlerDecorator), in addition it would be nice if the mutex not shared between command types - with my current code, the lock is static and would be shared between all types.
So onto my questions:
1) How can I apply TransactionCommandHandlerWithMutexDecorator to a specific command or commands, and use TransactionCommandHandlerDecorator for the rest - would i use the ExpressionBuilt event?
2) Would I need to create a new class for each command I wish to decorate (to ensure there is a unique lock object per command), or is there some beter way (using interception)?
Appreciate advice on the best way to do the above.
Thanks,
Chris
with my current code, the lock is static and would be shared between all types.
This is incorrect. Generic types don't share static members. For every TCommand
there will be a new static type. In other words a Decorator<int>
will have a different static object Lock
instance than a Decorator<string>
will have.
If you want it the other way around, thus have 1 single lock for all commands, this will be a bit harder. You can basically do three things:
But again, this is not what you want. The behavior you want is what happens by default, and this has nothing to do with the Simple Injector. That's how generics work in C# and .NET.
How can I apply TransactionCommandHandlerWithMutexDecorator to a specific command or commands, and use TransactionCommandHandlerDecorator for the rest
This is more easy than you might think. There is an RegisterDecorator
overload that takes in a Predicate<T>
, which allows you to tell when to decorate. This might look like this:
// Some helper methods
private static Type GetCommandType(Type handlerType)
{
return handlerType.GetGenericArguments()[0];
}
private static bool IsMutexCommand(Type commandType)
{
// Determine here is a class is a mutex command.
// Example:
return typeof(IMutexCommand).IsAssignableFrom(commandType);
}
// Decorator registration with predicates.
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerWithMutexDecorator<>),
c => IsMutexCommand(GetCommandType(c.ServiceType)));
// Decorator registration with predicates.
container.RegisterDecorator(
typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>),
c => !IsMutexCommand(GetCommandType(c.ServiceType)));
As everything with the Simple Injector the RegisterDecorator
is higly optimized. Under normal circumstances* the predicate will be called just once.
*It is possible the predicate is called multiple times, when multiple threads are simultaniously requesting that same instance for the same time, but after the built Expression is cached and the delegate is built, the predicate won't be called anymore.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With