Dmitriy Bobrovskiy
Dmitriy Bobrovskiy

Reputation: 153

MediatR doesn't handle contravariant notifications

In MediatR's documentation is said:

Containers that support generic variance will dispatch accordingly. For example, you can have an INotificationHandler<INotification> to handle all notifications.

But in my case it doesn't work. My question is how to make it working contravariant, so I don't have to create specific handler for each notification type?

I have hierarchy of models like this:

abstract record AuditRequestBase : INotification {}
abstract record SpecificAuditRequestBase : AuditRequestBase {}
record CreateSpecificPurchaseOrderAuditRequest : SpecificAuditRequestBase {}

where first two records are in separate library, connected to working project and last one is in the working project

and handler

class LogStorageHandler : INotificationHandler<SpecificAuditRequestBase>

but when I try to publish notification to mediator like this

_mediator.Publish(new CreateSpecificPurchaseOrderAuditRequest())

nothing happens. I went to source code of MediatR and it cannot resolve dependency INotificationHandler<SpecificAuditRequestBase>.

But if instead of one handler I create two handlers like

class LogStorageHandler : INotificationHandler<SpecificAuditRequestBase>
class LogStorageHandler2 : INotificationHandler<CreateSpecificPurchaseOrderAuditRequest>

then first handler (which is LogStorageHandler) starts handling that notification (same as LogStorageHandler2).

Startup file originally looked like this

services.AddMediatR(typeof(TStartup));

but then I tried to add another library as well, just in case - it didn't help

services.AddMediatR(typeof(TStartup), typeof(WatchGuardAuditRequestBase));

Versions: MediatR - 9.0.0
dotnet 5

Dependency Injection is embedded one.

Upvotes: 3

Views: 1634

Answers (1)

Jimmy Bogard
Jimmy Bogard

Reputation: 26765

This is an issue with your container, not MediatR or the DI extensions library. You'll want to register an open generic if you want the variance to kick in. Something like:

class LogStorageHandler<T> : INotificationHandler<T> 
    where T : SpecificAuditRequestBase

Now when the container looks for IEnumerable<INotificationHandler<T>> it will try to fill in the T above. You'll also need to register the open generic in the container, MediatR's DI extensions don't do that for you:

services.TryAddTransient(typeof(LogStorageHandler<>));

You can check out the unit tests of the MS DI project to understand what it supports in terms of these more complex scenarios. Otherwise, I isolate the container in a separate unit test from MediatR. MediatR simply defers to the container for registering dependencies through an IServiceProvider.

Upvotes: 7

Related Questions