jon*_*nho 1 c# dependency-injection simple-injector asp.net-mvc-4
我正在使用SimpleInjector 2.2.3.0.
我有一个MVC 4.0项目,并使用我的命令周围的装饰模式来管理我的UnitOfWork和我的授权.我有授权的装饰师 - IAuthorisationDecorator.这包裹了我的所有ITransactionalCommandHandlers.(每个ITransactionalCommandHandler也都装饰着IUnitOfWorkDecorator.)授权装饰器如下所示:
public void Handle(TCommand command)
{
//authorise this command for this Iprincipal
decoratedCommandHandler.Handle(command);
}
Run Code Online (Sandbox Code Playgroud)
我现在想要创建一个INonTransactionalCommandHandler(不需要NHibernate会话,它只是做一些文件IO).注意INonTransactionalCommandHandler和ITransactionalCommandHandler继承ICommandHandler- 看起来像这样:
public interface ICommandHandler<in TCommand, out TResult>
{
TResult Result { get; }
void Handle(TCommand command);
}
Run Code Online (Sandbox Code Playgroud)
我真的不想创建两个相同的AuthorisationDecorators(事务性/非事务性).因为最终它们装饰了一个基础ICommandHandler(实现为a ITransactional或INonTransactional).
所以我的问题是 - 有没有办法创建一个装饰基础的装饰器ICommandHandler- 但是容器知道要把它投射ICommandHandler到一个ITransactionalCommandHandler或一个INonTransactionalCommandHandler.有没有办法做到这一点?
如果您通过其ICommandHandler<TCommand, TResult>接口注册所有命令处理程序,则无法使用包装的装饰器来包装部分处理程序ITransactionalCommandHandler<TCommand, TResult>.如果处理程序由该ITransactionalCommandHandler<TCommand, TResult>接口显式注册,则只能应用此类装饰器,但这不是您应该执行的操作.您不希望这样,因为这意味着这些命令的使用者需要知道处理程序是否是事务性的,他们不应该关心它(这是一个实现细节).
因此,您的事务装饰器应该包装一个ICommandHandler<TCommand, TResult>.如果它需要访问ITransactionalCommandHandler<TCommand, TResult>接口,装饰器应该从中ICommandHandler转换输入参数ITransactionalCommandHandler.
您需要的是根据谓词注册装饰器,如下所示:
// 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<,>));
Run Code Online (Sandbox Code Playgroud)
谓词确保只有命令处理程序才能实现ITransactionalCommandHandler<,>.
您也可以使用属性标记处理程序,而不是使用该接口:
[Transactional]
class ShipOrderCommandHandler : ICommandHandler<ShipOrderCommand, Unit> { ... }
Run Code Online (Sandbox Code Playgroud)
在这种情况下,注册将成为:
container.RegisterDecorator(typeof(ICommandHandler<,>), typeof(TransactionDecorator<,>),
c => c.ImplementationType.GetCustomAttribute<TransactionalAttribute>() != null);
Run Code Online (Sandbox Code Playgroud)
另一种选择是使用接口标记命令本身或从基类继承:
public class ShipOrderCommand : ICommand<Unit>, ITransactionalCommand
{
public Guid OrderId;
}
Run Code Online (Sandbox Code Playgroud)
这允许您在其上添加泛型类型约束,TransactionDecorator<,>并允许您删除RegisterDecorator注册上的谓词:
public class TransactionDecorator<int TCommand, out TResult>
: ICommandHandler<TCommand, TResult>
where TCommand : ITransactionalCommand
{
// etc
}
Run Code Online (Sandbox Code Playgroud)
由于Simple Injector了解类型约束,因此您可以简化注册,如下所示:
container.RegisterDecorator(typeof(ICommandHandler<,>), typeof(TransactionDecorator<,>));
Run Code Online (Sandbox Code Playgroud)
请注意,在这种特殊情况下,我不建议使用最后一种方法,因为ITransactionalCommand再次在命令上实现接口意味着实现细节正在泄漏到命令的定义中.该命令及其使用者不需要知道命令处理程序是否需要事务(这可能在将来发生变化).