Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simple Injector using different decorators for different commands

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

like image 860
morleyc Avatar asked Feb 21 '23 03:02

morleyc


1 Answers

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:

  1. Derive the decorator from a non-generic base and define the lock there.
  2. Define the lock in a static non-generic class and reference that class from within the generic decorator.
  3. Move the lock to a non-generic class and inject this into the decorator as a normal dependency.

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.

like image 153
Steven Avatar answered Mar 15 '23 23:03

Steven