简单的注入器使用不同的装饰器来执行不同的命令

时间:2012-06-14 20:34:35

标签: c# .net inversion-of-control ioc-container simple-injector

您好我开始在我的所有项目中使用Simple Injector DI container,并希望就如何适应提供一些强有力的功能来满足我的要求。

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

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装饰器:

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)进行精确访问,此外如果在命令之间不共享互斥锁会很好types - 使用我当前的代码,锁是静态的,并且将在所有类型之间共享。

所以我的问题:

1)如何将TransactionCommandHandlerWithMutexDecorator应用于特定的一个或多个命令,并使用TransactionCommandHandlerDecorator进行其余的操作 - 我会使用ExpressionBuilt事件吗?

2)我是否需要为每个我想装饰的命令创建一个新类(以确保每个命令都有一个唯一的锁对象),或者是否有一些更好的方式(使用拦截)?

赞赏有关上述方法的最佳方法的建议。

谢谢,

克里斯

1 个答案:

答案 0 :(得分:8)

  

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

这是不正确的。 Generic types don't share static members。对于每个TCommand,都会有一个新的静态类型。换句话说,Decorator<int>将具有与static object Lock不同的Decorator<string>个实例。

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

  1. 从非通用基础派生装饰器并在那里定义锁定。
  2. 在静态非泛型类中定义锁,并从泛型装饰器中引用该类。
  3. 将锁移动到非泛型类,并将其作为普通依赖项注入装饰器。
  4. 但同样,这不是你想要的。您想要的行为是默认情况下发生的行为,这与Simple Injector无关。这就是泛型在C#和.NET中的工作方式。

      

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

    这比你想象的要容易得多。 RegisterDecorator有一个Predicate<T>重载,它允许您告诉何时进行装饰。这可能如下所示:

    // 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)));
    

    作为Simple Injector的一切,RegisterDecorator得到了高度优化。在正常情况下*谓词只会被调用一次。

    *可能多次调用谓词,当多个线程同时同时请求同一个实例时,但在缓存构建的表达式并构建委托之后,谓词将不再被调用。