【问题标题】:Autofac not resolving service with generic typeAutofac 不使用泛型类型解析服务
【发布时间】:2021-09-16 15:45:31
【问题描述】:

我有一个 .NET Core 3.1 通用主机,它使用中介者模式来处理消息:

public interface IMessageDispatcher {
  Task Dispatch<TMessage>(TMessage message) where TMessage : Message;
}

public class MessageDispatcher : IMessageDispatcher {
  private readonly IServiceProvider _container;

  public MessageDispatcher(IServiceProvider container) {
    _container = container;
  }

  public async Task Dispatch<TMessage>(TMessage message) {
    var handler = (IMessageHandler<TMessage>) _container.GetService(typeof(IMessageHandler<TMessage>));
    await handler.Handle(message);
  }
}

public interface IMessageHandler<TMessage> where TMessage : Message {
  Task Handle(TMessage message);
}

public class MyMessageHandler : IMessageHandler<MyMessage> {
  public Task Handle(MyMessage message) {
    // do stuff
  }
}

消息处理程序在 Autofac 中注册,如下所示:

private static void AutoRegisterMessageHandlers(ContainerBuilder builder, Assembly domainAssembly) {
  builder
    .RegisterAssemblyTypes(domainAssembly)
    .AsClosedTypesOf(typeof(IMessageHandler<>));
}

如果我按原样运行上述程序,那么当我调用Handle 方法时,对_container.GetService 的调用会返回一个带有明显NullReferenceException 的空值;但是,如果我在调用 Handle 之前设置断点,并在 Visual Studio 的即时窗口中运行以下命令:

_container.GetService(typeof(IMessageHandler<MyMessage>));

然后返回预期的消息处理程序,这表明消息处理程序已正确注册,只是当我尝试使用泛型类型解析消息处理程序时

_container.GetService(typeof(IMessageHandler<TMessage>));

事情出错了。我做错了什么吗?

更新

根据接受的答案,我从消息队列接收消息并反序列化它们,所以我没有具体的类型:

var message = Deserialiser.Deserialise<Message>(rawXmlFromQueue);
await Dispatch(message);

由于message 没有具体类型,Autofac 没有足够的信息来定位注册的消息处理程序,并返回null。然后解决方案是将消息转换为dynamic,并通过 C# 类型系统的魔力,足以将泛型类型转换为其具体实现:

var message = Deserialiser.Deserialise<Message>(rawXmlFromQueue);
await Dispatch((dynamic) message);

【问题讨论】:

  • 使用Service Locators 是一种反模式。见docs
  • 如果你正在做例如它是非常有效的。 CQRS 并且您不想将十几个命令/查询处理程序注入您的控制器;只要您知道通过额外的间接步骤让自己陷入困境。

标签: .net-core dependency-injection autofac


【解决方案1】:

您无法解析开放的泛型。

你提到的问题是你不能这样做:

container.GetService(typeof(ICommandHandler<TMessage>))

但 DI 并不是魔法,它只是另一种构造东西的方式。你也不能这样做:

new CommandHandler<TMessage>()

也就是说,除非您知道TMessage 是什么,否则这是没有意义的。你需要一个具体的类型。

但是,除此之外,很难弄清楚发生了什么。这个问题漏掉了很多。

  • 它提到对 container. GetService 的调用返回 null 但不是您在返回 null 时尝试解决的问题。
  • 所有示例代码都显示MessageHandler,但您在调试器中运行的示例是CommandHandler。还不足以知道这是一个错字还是问题的一部分。

无论如何,我希望这能给你一些关于看什么的想法。

【讨论】:

  • 抱歉,CommandHandler 是一个错字;我已经解决了。
  • 命令正在从消息队列中反序列化,即使这样它也只是被反序列化为Message,而不是具体类型。所以我认为这就是我的问题所在,正如你所说。如果我看例如message.GetType() 那么它确实是 MyMessage,但我猜 Autofac 没有足够的信息来获取预期的消息处理程序。有关如何使该特定圆成正方形的任何建议?
  • 听起来你需要对message.GetType()Type.MakeGenericType 做一些反射工作来创建正确的封闭泛型来解决。那是应用程序代码,而不是 DI 会以某种方式桥接的东西。
  • 没关系,我在stackoverflow.com/a/56668756/319980 找到了答案 - 将反序列化的消息转换为dynamic 在调用Dispatch(TMessage) 方法之前
猜你喜欢
  • 1970-01-01
  • 2022-06-10
  • 1970-01-01
  • 1970-01-01
  • 2014-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-27
相关资源
最近更新 更多