【问题标题】:Simple injector open generic decorators简单的注入器打开通用装饰器
【发布时间】:2012-11-06 07:21:23
【问题描述】:

我正在尝试利用简单注射器中的一些不错的功能。

我目前在使用装饰器时遇到问题,但我期望它们也没有受到影响。

我是这样注册的:

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&lt;CreateProductCommand&gt; 的调用会在运行之前调用 CreateValidFriendlyUrlCommandHandler&lt;&gt;CreateProductValidationCommandHandler&lt;&gt;

我尝试过这样的不同注册:

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

CreateProductValidationCommandHandlerCreateValidFriendlyUrlCommandHandler 实现ICommandHandler&lt;CreateProductCommand&gt; 时,我认为在ICommandHandler&lt;CreateProductCommand&gt; 类型上为ICommandHandler&lt;CreateProductCommand&gt; 注册一个装饰器可能会遇到一些循环引用。

但改变这一点并没有什么不同。

这是我的CreateProductValidationCommandHandler&lt;TCommand&gt;

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&lt;TCommand&gt;:

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

【问题讨论】:

  • 你的装饰器不应该是通用的。事实上,从技术上讲,它们不是装饰器 :-)
  • 请注意Appdomain.CurrentDomain.GeAssemblies 只返回已经加载的程序集。如果所有实现都与ICommandHandler&lt;T&gt; 在同一个程序集中,那不会有问题,但要小心。

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


【解决方案1】:

问题在于 Simple Injector 永远无法用您的装饰器之一包装 ICommandHandler&lt;T&gt; 实现,因为存在无法解析的泛型类型 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 参数(并且没有说明 @987654330 @ 是 TCommand)。

事实上,您根本没有创建装饰器。装饰器包装了它实现的同一接口的实例。在实现ICommandHandler&lt;CreateProductCommand&gt; 时包装ICommandHandler&lt;TCommand&gt;。实现此功能的唯一方法是明确指定 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

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

【讨论】:

  • 好吧,这是有道理的,我所拥有的一些东西是我试图让它按我想要的方式工作。我有一个开放的通用注册并想要一些装饰器,但我只希望在命令是特定类型时应用它们。不过,我似乎以错误的方式解决了这件事。谢谢:)
  • 现在我醒了,我又开始尝试了,一旦我停止使用 RegisterManyForOpenGeneric 并按照建议放弃 TCommand,我现在可以让它工作了 :)
  • @DavidMcLean:没有理由停止使用RegisterManyForOpenGeneric,因为它一次性注册了开放通用接口的所有实现。没有好的选择。
  • @DavidMcLean:另外请注意,我认为您设法通过了RegisterDecorator 方法的参数验证过程。我目前正在考虑改进这一点,以便抛出 ArgumentException 而不是静默接受类型,但永远无法包装类型。感谢您提出这个问题。
  • RE RegisterManyForOpenGeneric - 我遇到了一个问题,因为我有多个 ICommandHandler 的实现,所以它只需要一个。我在上面所做的很多事情都是试图解决这个错误。一旦我接受了我可以注册我想要的实现然后装饰它,我很快就得到了一个可行的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-05
  • 2012-06-17
  • 2019-12-07
  • 2017-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多