4

嗨,我开始在我的所有项目中使用Simple Injector DI 容器,并且想要一些关于如何适应我的要求的强大功能的建议。

我有几个命令处理程序装饰器将包装命令:

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

互斥装饰器:

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

在某些情况下,仅以这种阻塞方式包装一些命令(通过使用 TransactionCommandHandlerWithMutexDecorator)才有意义,并允许其他人在所有线程中不受限制地访问(使用 TransactionCommandHandlerDecorator),此外,如果互斥锁不在命令类型之间共享会很好 - 使用我当前的代码,锁是静态的,将在所有类型之间共享。

所以关于我的问题:

1) 我如何将 TransactionCommandHandlerWithMutexDecorator 应用于特定的命令或命令,其余的使用 TransactionCommandHandlerDecorator - 我会使用 ExpressionBuilt 事件吗?

2)我是否需要为我希望装饰的每个命令创建一个新类(以确保每个命令都有一个唯一的锁定对象),还是有更好的方法(使用拦截)?

感谢有关执行上述操作的最佳方法的建议。

谢谢,

克里斯

4

1 回答 1

8

使用我当前的代码,锁是静态的,将在所有类型之间共享。

这是不正确的。泛型类型不共享静态成员。每一个TCommand都会有一个新的静态类型。换句话说,a Decorator<int>will 具有与 a will 不同的static object Lock实例Decorator<string>

如果你想要它反过来,因此对所有命令都有一个单一的锁,这会有点困难。你基本上可以做三件事:

  1. 从非通用基础派生装饰器并在那里定义锁。
  2. 在静态非泛型类中定义锁,并从泛型装饰器中引用该类。
  3. 将锁移动到非泛型类,并将其作为正常依赖注入到装饰器中。

但同样,这不是你想要的。您想要的行为是默认情况下发生的,这与 Simple Injector 无关。这就是泛型在 C# 和 .NET 中的工作方式。

如何将 TransactionCommandHandlerWithMutexDecorator 应用于特定的命令或命令,其余的使用 TransactionCommandHandlerDecorator

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.

于 2012-06-14T21:02:56.030 回答