Am writing a generic message handler and need to get various message handlers via AutoFac. The basic definition of the message handler is:
public interface IMessageHandler<in TMessage> :
IMessageHandler
where TMessage : IMessage
{
Task<IMessageResult> Handle(TMessage message);
}
I have also defined a marker interface so that these can be easily registered in AutoFac
public interface IMessageHandler
{
}
A Sample message handler is:
public class CreatedEventHandler : IMessageHandler<CreatedEvent>
{
public Task<IMessageResult> Handle(CreatedEvent message)
{
// ...
}
}
And these are nicely registered named via Autofac using
builder.RegisterAssemblyTypes(assemblies)
.Where(t => typeof(IMessageHandler).IsAssignableFrom(t))
.Named<IMessageHandler>(t => t.Name.Replace("Handler", string.Empty))
.InstancePerLifetimeScope();
This all works fine. However, when I need to resolve a handler, i have an issue
// handler returned is non null and of type marker interface IMessageHandler
var handler = container.Resolve("CreatedEvent");
// This is null. I just can't understand why
var createdEventHander = handler as IMessageHandler<IMessage>;
Why is the cast above returns null? Even though the contravariance is defined in the IMessageHandler<>
interface.
How can I resolve the appropriate handlers?
Thanks
Oops!
// Covariance
handler as IMessageHandler<IMessage>;
Your handler
has a generic argument that isn't IMessage
but an IMessage
implementation. Thus, this is covariance (you're upcasting a generic argument).
Since I don't know your actual software architecture I can't provide you a solution. At least, you know why the whole cast results in null
.
Your message handlers could both implement IMessageHandler<ConcreteEvent>
and a new non-generic interface IMessageHandler
:
public interface IMessageHandler
{
Task<IMessageResult> Handle(IMessage message);
}
public interface IMessageHandler<TMessage> : IMessageHandler
where TMessage : IMessage
{
Task<IMessageResult> Handle(TMessage message);
}
public class CreatedEventHandler : IMessageHandler<CreatedEvent>
{
public Task<IMessageResult> Handle(CreatedEvent message)
{
// ...
}
// I would implement the non-generic Handle(IMessage) explicitly
// to hide it from the public surface. You'll access it when successfully
// casting a reference to IMessageHandler
Task<IMessageResult> IMessageHandler.Handle(IMessage message)
{
return Handle((CreatedEvent)message);
}
}
Now the whole cast will work because your classes will explicitly implement IMessageHandler<IMessage>
.
And to avoid repeating yourself too much, you can implement an abstract class:
public abstract class MessageHandler<TMessage> : IMessageHandler<TMessage>
where TMessage : IMessage
{
public abstract Task<IMessageResult> Handle(TMessage message);
// I would implement the non-generic Handle(IMessage) explicitly
// to hide it from the public surface. You'll access it when successfully
// casting a reference to IMessageHandler
Task<IMessageResult> IMessageHandler.Handle(IMessage message)
{
return Handle((TMessage)message);
}
}
Finally, your concrete message handlers would look as follows:
public class CreatedEventHandler : MessageHandler<CreatedEvent>
{
public Task<IMessageResult> Handle(CreatedEvent message)
{
// ...
}
}
That is, your cast can be turned to just handler as IMessageHandler
.