Here is the sender and handler interfaces:
public interface ISender
{
Task SendAsync(object e);
}
public interface IHandler<in TEvent>
{
Task HandleAsync(TEvent e);
}
So I register in IoC container a sender service implementation, which dispatches events to all the compatible IHandler<in T>
implementations. I use Autofac with a contravariance source, but there could be something else:
[Service]
public class Sender : ISender
{
public Sender(IServiceProvider provider) => Provider = provider;
IServiceProvider Provider { get; }
public async Task SendAsync(object e)
{
var eventType = e.GetType();
var handlerType = typeof(IHandler<>).MakeGenericType(eventType);
var handlerListType = typeof(IEnumerable<>).MakeGenericType(handlerType);
var method = handlerType.GetMethod("HandleAsync", new[] { eventType });
var handlers = ((IEnumerable)Provider.GetService(handlerListType)).OfType<object>();
await Task.WhenAll(
handlers.Select(h =>
Task.Run(() => (Task)method.Invoke(h, new[] { e }))
.ContinueWith(_ => { })));
}
}