简单的注入器打开通用装饰器

时间:2012-11-17 23:09:49

标签: c# .net dependency-injection ioc-container simple-injector

我正在尝试使用简单注入器中的一些很好的功能。

我目前在修饰器方面遇到问题,但是当我期待它们时它们也没有被击中。

我正在这样注册:

container.RegisterManyForOpenGeneric(
      typeof(ICommandHandler<>),
      AppDomain.CurrentDomain.GetAssemblies());

container.RegisterDecorator(
      typeof(ICommandHandler<>),
      typeof(CreateValidFriendlyUrlCommandHandler<>),
      context => context.ServiceType == typeof(ICommandHandler<CreateProductCommand>)
 );

 container.RegisterDecorator(
      typeof(ICommandHandler<>),
      typeof(CreateProductValidationCommandHandler<>),
      context => context.ServiceType == typeof(ICommandHandler<CreateProductCommand>)
 );

我认为我必须遗漏一些内容,因为我希望拨打ICommandHandler<CreateProductCommand>之前会先调用CreateValidFriendlyUrlCommandHandler<>CreateProductValidationCommandHandler<>

我尝试了不同的注册:

container.RegisterManyForOpenGeneric(
      typeof(ICommandHandler<>),
      AppDomain.CurrentDomain.GetAssemblies());

container.RegisterDecorator(
      typeof(ICommandHandler<>),
      typeof(CreateValidFriendlyUrlCommandHandler<>),
      context => context.ImplementationType == typeof(CreateProductCommandHandler)
 );

 container.RegisterDecorator(
      typeof(ICommandHandler<>),
      typeof(CreateProductValidationCommandHandler<>),
      context => context.ImplementationType == typeof(CreateProductCommandHandler)
 );

正如我想在ICommandHandler<CreateProductCommand>ICommandHandler<CreateProductCommand>实施CreateProductValidationCommandHandler时,CreateValidFriendlyUrlCommandHandler类型ICommandHandler<CreateProductCommand>上的CreateProductValidationCommandHandler<TCommand>注册装扮器可能会发生一些问题参考

但改变那没有区别。

这是我的public class CreateProductValidationCommandHandler<TCommand> : ICommandHandler<CreateProductCommand> { private readonly ICommandHandler<TCommand> decorated; private readonly IValidationService validationService; public CreateProductValidationCommandHandler( ICommandHandler<TCommand> decorated, IValidationService validationService) { this.decorated = decorated; this.validationService = validationService; } public void Handle(CreateProductCommand command) { if (!validationService.IsValidFriendlyName( command.Product.ProductFriendlyUrl)) { command.ModelStateDictionary.AddModelError( "ProductFriendlyUrl", "The Friendly Product Name is not valid..."); return; } if (!validationService.IsUniqueFriendlyName( command.Product.ProductFriendlyUrl)) { command.ModelStateDictionary.AddModelError( "ProductFriendlyUrl", "The Friendly Product Name is ..."); return; } } }

CreateValidFriendlyUrlCommandHandler<TCommand>

这是我的public class CreateValidFriendlyUrlCommandHandler<TCommand> : ICommandHandler<CreateProductCommand> { private readonly ICommandHandler<TCommand> decorated; public CreateValidFriendlyUrlCommandHandler(ICommandHandler<TCommand> decorated) { this.decorated = decorated; } public void Handle(CreateProductCommand command) { if (string.IsNullOrWhiteSpace( command.Product.ProductFriendlyUrl)) { command.Product.ProductFriendlyUrl = MakeFriendlyUrl(command.Product.Name); } } }

{{1}}

1 个答案:

答案 0 :(得分:3)

问题是Simple Injector永远无法用你的一个装饰器包装ICommandHandler<T>实现,因为有一个不可解析的泛型类型TCommand。如果装饰器的Handle方法调用decorated实例,您会注意到这一点。例如:

public class CreateValidFriendlyUrlCommandHandler<TCommand>
    : ICommandHandler<CreateProductCommand>
{
    private readonly ICommandHandler<TCommand> decorated;

    public CreateValidFriendlyUrlCommandHandler(
        ICommandHandler<TCommand> decorated)
    {
        this.decorated = decorated;
    }

    public void Handle(CreateProductCommand command)
    {
        // This won't compile since CreateProductCommand and
        // TCommand are not related.
        this.decorated.Handle(command);
    }
}

此代码无法编译,因为装饰器的Handle方法采用CreateProductCommand参数,而decorated实例采用TCommand参数,未指定(并且没有说明CreateProductCommandTCommand)。

实际上你根本没有创建装饰器。装饰器包装它实现的相同接口的实例。在实施ICommandHandler<TCommand>时包裹ICommandHandler<CreateProductCommand>。只有当您明确指定TCommandCreateProductCommand时,才能实现此目的的唯一方法如下:

ICommandHandler<CreateProductCommand> handler = 
    new CreateValidFriendlyUrlCommandHandler<CreateProductCommand>(
        new CreateProductCommandHandler()
    );

但是,Simple Injector无法“猜测”这个TCommand应该是CreateProductCommand,这就是为什么你的“装饰者”没有被包裹的原因。

长话短说:放弃TCommand

public class CreateValidFriendlyUrlCommandHandler
    : ICommandHandler<CreateProductCommand>
{
    private ICommandHandler<CreateProductCommand> decorated;

    public CreateValidFriendlyUrlCommandHandler(
        ICommandHandler<CreateProductCommand> decorated)
    {
        this.decorated = decorated;
    }

    public void Handle(CreateProductCommand command)
    {
        // logic here
    }
}

或者使用类型约束使其具有通用性:

   public class CreateValidFriendlyUrlCommandHandler<TCommand>
        : ICommandHandler<TCommand>
        where TCommand : CreateProductCommand
    {
        private ICommandHandler<TCommand> decorated;

        public CreateValidFriendlyUrlCommandHandler(
            ICommandHandler<TCommand> decorated)
        {
            this.decorated = decorated;
        }

        public void Handle(TCommand command)
        {
            // logic here
        }
    }

或删除类型约束并允许处理任何类型的命令,而不仅仅是CreateProductCommand

请注意,如果要定义许多只能处理一种特定类型命令处理程序的装饰器,您可能需要重新考虑策略。您的设计可能存在问题。