Here's a complete copy of the question:
Please find below an explanation of the problem, and a simple but almost complete repro (.Net 8 / MediatR 12.4.1), usable in Linqpad or Console app.
I have a barely complex hierarchy of Notifications classes with MediatR :
public abstract class EntityNotification : INotification { }
public class EntityNotificationTyped<T> : EntityNotification { }
public class A { }
public class B { }
(INotification -> EntityNotification -> EntityNotificationTyped<T>). I have also other children under EntityNotificationTyped but like it appears above, it's already enough to exhibit the issue.
Please note that "EntityNotification" is not by itself a generic type.
Here are the handlers :
public class GenericNotificationHandler
: INotificationHandler<EntityNotification>
{
public async Task Handle(EntityNotification notification, CancellationToken cancellationToken)
{
Console.WriteLine("GenericNotificationHandler received:" + notification.GetType().FullName);
}
}
public class EntityNotificationForAHandler
: INotificationHandler<EntityNotificationTyped<A>>
{
public async Task Handle(EntityNotificationTyped<A> notification, CancellationToken cancellationToken)
{
Console.WriteLine("EntityNotificationForAHandler received:" + notification.GetType().FullName);
}
}
public class EntityNotificationForBHandler
: INotificationHandler<EntityNotificationTyped<B>>
{
public async Task Handle(EntityNotificationTyped<B> notification, CancellationToken cancellationToken)
{
Console.WriteLine("EntityNotificationForBHandler received:" + notification.GetType().FullName);
}
}
Here is the test program:
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddMediatR(config =>
{
config.RegisterServicesFromAssemblyContaining<Program>();
});
var app = builder.Build();
var publisher = app.Services.GetRequiredService<IPublisher>();
Console.WriteLine("Publishing A");
await publisher.Publish(new EntityNotificationTyped<A>());
Console.WriteLine(Environment.NewLine + "Publishing B");
await publisher.Publish(new EntityNotificationTyped<B>());
Console.WriteLine("End");
If I run it as-is, it works fine, all handlers are called as expected :
Publishing A
GenericNotificationHandler received:EntityNotificationTyped`1[[A, MediatRPublish, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
EntityNotificationForAHandler received:EntityNotificationTyped`1[[A, MediatRPublish, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
Publishing B
GenericNotificationHandler received:EntityNotificationTyped`1[[B, MediatRPublish, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
EntityNotificationForBHandler received:EntityNotificationTyped`1[[B, MediatRPublish, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
End
But now, if I completely remove "EntityNotificationForBHandler" class, no handler is invoked for "Publishing B" :
Publishing A
GenericNotificationHandler received:EntityNotificationTyped`1[[A, MediatRPublish, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
EntityNotificationForAHandler received:EntityNotificationTyped`1[[A, MediatRPublish, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]
Publishing B
End
Why my "GenericNotificationHandler" that was previously working for any type of notification is not working when there's no specific handler for the "most nested inherited" notification type, besides it works when the last one is present ?
I have noticed that making the handler itself generic seems to help, I guess it has something to do with covariance/contravariance, but I don't get why sometimes it works, sometimes not. Here's a possible "generic handler" :
public class GenericNotificationHandler<TEntityNotification>(ILogger<GenericNotificationHandler<TEntityNotification>> logger)
: INotificationHandler<TEntityNotification>
where TEntityNotification : EntityNotification
{
public async Task Handle(TEntityNotification notification, CancellationToken cancellationToken)...
PS : I have also noticed that if I register explicitly the Generichandler for EntityNotificationTyped it also works: builder.Services.AddTransient<INotificationHandler<EntityNotificationTyped<B>>, GenericNotificationHandler>(); But the idea of a generic handler was to be able to capture all kind of notifications without to have to register them explicitly.
I hope you'll be able to help me to find the best solution to handle this kind of situation.
Hello Jimmy,
I am using occasionally MediatR since a few years, have recently shared a question on StackOverflow regarding an issue with Notification publishing/handling, and comments suggested me to share it directly here. Here's the original link : https://stackoverflow.com/questions/79125876/handlers-are-not-called-for-some-notifications-published-with-mediatr-depending
Here's a complete copy of the question: Please find below an explanation of the problem, and a simple but almost complete repro (.Net 8 / MediatR 12.4.1), usable in Linqpad or Console app.
I have a barely complex hierarchy of Notifications classes with MediatR :
(
INotification
->EntityNotification
->EntityNotificationTyped<T>
). I have also other children underEntityNotificationTyped
but like it appears above, it's already enough to exhibit the issue.Please note that "EntityNotification" is not by itself a generic type.
Here are the handlers :
Here is the test program:
If I run it as-is, it works fine, all handlers are called as expected :
But now, if I completely remove "EntityNotificationForBHandler" class, no handler is invoked for "Publishing B" :
Why my "GenericNotificationHandler" that was previously working for any type of notification is not working when there's no specific handler for the "most nested inherited" notification type, besides it works when the last one is present ?
I have noticed that making the handler itself generic seems to help, I guess it has something to do with covariance/contravariance, but I don't get why sometimes it works, sometimes not. Here's a possible "generic handler" :
PS : I have also noticed that if I register explicitly the Generichandler for EntityNotificationTyped it also works:
builder.Services.AddTransient<INotificationHandler<EntityNotificationTyped<B>>, GenericNotificationHandler>();
But the idea of a generic handler was to be able to capture all kind of notifications without to have to register them explicitly.I hope you'll be able to help me to find the best solution to handle this kind of situation.
Regards