David Keaveny
David Keaveny

Reputation: 4079

Autofac not resolving service with generic type

I have a .NET Core 3.1 generic host that uses the mediator pattern to handle messages:

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

The message handlers are registered with Autofac, as follows:

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

If I run the above as-is, then the call to _container.GetService returns a null with the obvious NullReferenceException when I call the Handle method; however, if I put a breakpoint in before I call Handle, and run the following from the Immediate window in Visual Studio:

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

then the expected message handler is returned, which suggests that the message handler is registered properly, it's just that when I try to resolve the message handler using the generic type

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

that things go wrong. Is there anything I'm doing obviously wrong?

Update

As per the accepted answer, I was receiving my messages from a message queue and deserialising them, so I had no concrete type:

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

Since there was no concrete type for message, Autofac didn't have enough information to locate the registered message handler, and returned null. The solution then was to cast the message to dynamic, and through the magic of C#'s type system, that was sufficient to convert the generic type into its concrete implementation:

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

Upvotes: 0

Views: 540

Answers (1)

Travis Illig
Travis Illig

Reputation: 23894

You can't resolve an open generic.

The problem you mention is you can't do this:

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

But DI isn't magic, it's just another way to construct stuff. You also can't do:

new CommandHandler<TMessage>()

That is, unless you know what TMessage is, this doesn't make sense. You need a concrete type there.

However, beyond that, it's hard to figure out what's going on. The question is missing a lot.

  • It mentions the call to container. GetService is returning null but not what you're trying to resolve when it's returning null.
  • All the sample code shows MessageHandler but the examples you're running in the debugger are CommandHandler. There's not enough to know if that's a typo or part of the problem.

In any case, I hope that gives you some ideas on what to look at.

Upvotes: 1

Related Questions