【问题标题】:Can I make a keyed service for AutoFac based on the type of the Open Generic我可以根据 Open Generic 的类型为 AutoFac 提供密钥服务吗
【发布时间】:2012-11-29 23:11:03
【问题描述】:

我想为我的 Open Generic ICommandHandler 创建一个基于 T 的 keyedService。当 ICommandHandler 有一个继承自 ConsultantCommand 的 T 时,我想注册一个 ConsultatCommandHanlder 键控服务

知道怎么做吗?或者如果它甚至可能?我是 AutoFac 的新手,正在苦苦挣扎。

我目前正在像这样注册 CommandHandler:

        //Register All Command Handlers
        builder.RegisterAssemblyTypes(assemblies)
          .As(
              t =>
              t.GetInterfaces()
               .Where(a => a.IsClosedTypeOf(typeof (ICommandHandler<>)))
               .Select(a => new KeyedService("commandHandler", a))).InstancePerHttpRequest();

如果可能的话,我猜我必须在获得 Closed Type 时识别 CommandHandlers,并以某种方式识别 Command 实现 ConsultantCommand 的那些。

我试过了:

        builder.RegisterAssemblyTypes(assemblies)
               .As(
                 t =>
                   t.GetInterfaces()
                    .Where(a => a.IsClosedTypeOf(typeof(ICommandHandler<>)) &&
                        a.GetGenericArguments()[0].IsSubclassOf(typeof(ConsultantCommand)))
                    .Select(a => new KeyedService("ConsultantCommandHandler", a))).InstancePerHttpRequest();

但不是喜悦似乎不起作用。它可以编译,但现在没有注册 CommandHandlers,即使是那些继承自 ConsultantCommand 的。我认为我的语法都是错误的

【问题讨论】:

  • 您能否向我们提供更多背景信息,说明您为什么要这样做?
  • 我想在成功完成之后或者可能在它们运行之前装饰某些类别的命令,以便我可以在系统中执行我们的某些操作。例如电子邮件或清除缓存
  • 您尝试做的事情对于事件处理程序来说似乎更合理。命令和处理程序之间通常应该存在一一对应的关系。与其将逻辑拆分为多个命令处理程序,不如创建一个依赖于包含该逻辑的多个服务的处理程序。
  • 是的。我正在使用 Udi Dahans 域事件模式。因此,如果一个人被更新,它会引发一个名为 PersonUpdated 的事件,并且事件处理程序可以监听并清除缓存发送电子邮件等。作为感兴趣的问题,Simple Injector 是否可以有条件地添加一个装饰器?顺便说一句,感谢您编写的命令文章非常有助于解释命令/处理程序模式的逻辑以及拆分给我们团队的原因。
  • 不客气。 Simple Injector 拥有 IMO 对所有容器的条件装饰器的最佳支持。您可以使用 RegisterDecorator 重载来接受谓词。每个封闭类型只会对谓词进行一次评估。

标签: dependency-injection inversion-of-control decorator autofac command-pattern


【解决方案1】:

首先,您需要确保您的 ICommandHandler&lt;T&gt; 被声明为支持协方差:

public interface ICommandHandler<out T> { }

out 很重要,否则您将无法一次解析所有 ConsultantCommand 处理程序。你还会得到一个 Autofac 异常。

接下来,使用Named 扩展方法来注册您的命名服务,而不是自己做。语法如下所示:

builder.RegisterAssemblyTypes(assemblies)
       .Where(t =>
                t.IsClosedTypeOf(typeof(ICommandHandler<>)) &&
                t.GetInterfaces()[0]
                 .GetGenericArguments()[0]
                 .IsAssignableTo<ConsultantCommand>())
       .Named("name", typeof(ICommandHandler<ConsultantCommand>))
       .InstancePerHttpRequest();

这将所有ICommandHandler&lt;T&gt; 的服务注册为ICommandHandler&lt;ConsultantCommand&gt;,其中T 派生自ConsultantCommand。您必须使用基本类型,否则您将无法一次解析所有处理程序。没有任何方法可以“解析从该基类型派生的所有服务”。也无法解析开放泛型列表。

当您解析处理程序列表时,您需要解析一个名为IEnumerable&lt;T&gt;

using(var scope = container.BeginLifetimeScope())
{
  var x =
    scope.ResolveNamed<IEnumerable<ICommandHandler<ConsultantCommand>>>("name");
}

当然,您使用的是InstancePerHttpRequest,所以它更像是:

var x =
  AutofacDependencyResolver
    .Current
    .RequestLifetimeScope
    .ResolveNamed<IEnumerable<ICommandHandler<ConsultantCommand>>>("name");

如上所述,您必须注册为封闭的泛型,因为这不起作用:

// WON'T WORK:
scope.ResolveNamed<IEnumerable<ICommandHandler<>>>("name");

您可以根据需要修改注册等。其余的注册扩展应该像往常一样工作 - 如果你想将事物注册为实现的接口或其他任何东西,它应该与 RegisterAssemblyTypes 一起工作,就像你对单个服务所做的那样。

【讨论】:

  • 看起来棒极了,感谢您抽出宝贵的时间来编写它。我只需要能够装饰这些命令,所以我应该能够做到这一点。如果我想在 MVC 控制器中为命令处理程序使用构造函数注入,例如ICommandHandler 我还能做到吗?
  • [不通过编译器运行] 如果您不仅使用“命名”而且还在注册时抛出“As()”,那么您应该能够做到这一点。基本上,您需要将每个命令处理程序注册为两件事 - 首先是命名服务(用于您的集合),其次是您刚才提到的更具体的 ICommandHandler。 “As”语法选项之一应该涵盖您。
  • 我的 ICommandHandler 接口有一个 void 方法 Handle(TCommand command) 所以我不能声明支持协方差。认为我只是想强迫一些可能不应该做的事情。
  • 很遗憾听到它不会像您计划的那样工作。如果此答案或其他答案之一为您提供了确定该问题所需的信息,您可以考虑将其标记为答案。
  • 是的,如果您不知道如何让 AutoFac 做到这一点,那么这是不可能的。我刚刚在所有 CommandHandlers 周围注册了一个装饰器,然后在那里测试了类型。没有我想要的那么干净,但最好看起来可能
【解决方案2】:

你想要:

builder.RegisterAssemblyTypes(assemblies)
       .AsClosedTypesOf(typeof(ICommandHandler<>))
       .AsSelf()
       .AsImplementedInterfaces()
       .InstancePerHttpRequest();

【讨论】:

  • 抱歉 Autofac 的新手 这不只是注册了所有的 ICommandHandlers 吗?我怎么知道哪些是 ICommandHandler 其中 T 从 ConsultantCommand 继承?
猜你喜欢
  • 1970-01-01
  • 2015-11-05
  • 2020-11-23
  • 2010-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-08
  • 1970-01-01
相关资源
最近更新 更多