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);
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.
container. GetService
is returning null but not what you're trying to resolve when it's returning null.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.