When using interfaced notifications, and handlers for the interfaces - the handler does not run unless there exists a handler targetting a concrete implementation of the respective interface.
Steps to reproduce:
Bootstrap
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMediatR(c => c.RegisterServicesFromAssembly(typeof(Program).Assembly));
using IHost host = builder.Build();
var mediator = host.Services.GetRequiredService<IMediator>();
A) A simple notification handler that runs
// Simple case, nothing new
record SimpleGreeterNotification(string Message) : INotification;
class SimpleHandler : INotificationHandler<SimpleGreeterNotification>
{
public Task Handle(SimpleGreeterNotification notification, CancellationToken cancellationToken)
{
Console.WriteLine(notification.Message);
return Task.CompletedTask;
}
}
// ...
await mediator.Publish(new SimpleGreeterNotification("Hello"));
// Outputs "Hello", no surprises here
B) An interfaced notification handler that runs
// Interfaced case
public interface IGreeterInterface : INotification;
record ImplementingGreeterNotification(string Message) : IGreeterInterface;
class InterfacedGreeterHandler : INotificationHandler<IGreeterInterface>
{
public Task Handle(IGreeterInterface notification, CancellationToken cancellationToken)
{
Console.WriteLine("I only run when ImplementingGreeterHandler exists");
return Task.CompletedTask;
}
}
class ImplementingGreeterHandler : INotificationHandler<ImplementingGreeterNotification>
{
public Task Handle(ImplementingGreeterNotification notification, CancellationToken cancellationToken)
{
Console.WriteLine(notification.Message);
return Task.CompletedTask;
}
}
// ...
await mediator.Publish(new ImplementingGreeterNotification("Hello interface"));
// Outputs "I only run when ImplementingGreeterHandler exists"
// Outputs "Hello interface
C) Now for the interfaced notification handler that does not run
// Interfaced case
public interface IGreeterInterface : INotification;
record ImplementingGreeterNotification(string Message) : IGreeterInterface;
class InterfacedGreeterHandler : INotificationHandler<IGreeterInterface>
{
public Task Handle(IGreeterInterface notification, CancellationToken cancellationToken)
{
Console.WriteLine("I only run when ImplementingGreeterHandler exists");
return Task.CompletedTask;
}
}
// ...
await mediator.Publish(new ImplementingGreeterNotification("Hello interface"));
// No output
Cases A and B are as expected, whereas case C is strange - I would expect InterfacedGreeterHandler to be run.
Further notes
I am not sure any of this is relevant, but I did some investigating.
I have a theory that this issue is caused near NotificationHandlerWrapper. Inspecting the service collection, I verify that it correctly contains INotificationHandler<IGreeterInterface>, but it does not contain INotificationHandler<ImplementingGreeterNotification>.
It does make sense I suppose, but at face value this looks like odd behavior either way.
I tested a more basic, yet potentially related case; implementing a catch-all INotificationHandler<INotification> never runs if there are no other INotificationHandler-types registered.
The issue at hand
When using interfaced notifications, and handlers for the interfaces - the handler does not run unless there exists a handler targetting a concrete implementation of the respective interface.
Steps to reproduce:
Bootstrap
A) A simple notification handler that runs
B) An interfaced notification handler that runs
C) Now for the interfaced notification handler that does not run
Cases A and B are as expected, whereas case C is strange - I would expect
InterfacedGreeterHandler
to be run.Further notes
I am not sure any of this is relevant, but I did some investigating.
I have a theory that this issue is caused near
NotificationHandlerWrapper
. Inspecting the service collection, I verify that it correctly containsINotificationHandler<IGreeterInterface>
, but it does not containINotificationHandler<ImplementingGreeterNotification>
.From these lines I see that
IServiceProvider.GetServices<INotificationHandler<ImplementingGreeterNotification>>
does not return anything, which I suspect subsequently fails to resolve a respectiveNotificationHandlerExecutor
. https://github.com/jbogard/MediatR/blob/cac76bee59a0f61c99554d76d4c39d67810fb406/src/MediatR/Wrappers/NotificationHandlerWrapper.cs#L24-L26It does make sense I suppose, but at face value this looks like odd behavior either way.
I tested a more basic, yet potentially related case; implementing a catch-all
INotificationHandler<INotification>
never runs if there are no otherINotificationHandler
-types registered.