# How To Publish MediatR Notifications In Parallel

> MediatR supports the publish-subscribe pattern with notifications, and until recently the handlers subscribing to an INotification could only execute serially, one by one. In this week's newsletter, I'll show you how to configure MediatR to execute the handlers in parallel.

Published: 2023-03-25. Author: Milan Jovanović.

Canonical: https://milanjovanovic.tech/blog/how-to-publish-mediatr-notifications-in-parallel

MediatR v12 added the `INotificationPublisher` interface, which controls how notification handlers are invoked.
The default `ForeachAwaitPublisher` awaits each handler one by one.
Swap in `TaskWhenAllPublisher` through `AddMediatR`, and the handlers run together with `Task.WhenAll`, but they still share one service scope.

**MediatR** is a popular library with a simple **mediator pattern** implementation in .NET.

Here's a definiton taken from MediatR's GitHub: **"In-process messaging with no dependencies."**

With the rise in popularity of the [**CQRS pattern**](https://milanjovanovic.tech/blog/cqrs-pattern-with-mediatr), MediatR became the go-to library to implement commands and queries.

However, MediatR also has support for the **publish-subscribe** pattern using notifications.
You can publish an `INotification` instance and have multiple subscribers handle the published message.

Until recently, the handlers subscribing to an `INotification` message could only execute serially, one by one.

In this week's newsletter, I'll show you how to configure MediatR to **execute the handlers in parallel**.

Let's dive in.

## How Publish-Subscribe Works With MediatR

Before I talk about notification publishing strategies, let's see how **publish-subscribe** works with **MediatR**.

You need a class implementing the `INotification` interface:

```csharp
public record OrderCreated(Guid OrderId) : INotification;
```

Then you need a respective `INotificationHandler` implementation:

```csharp
public class OrderCreatedHandler : INotificationHandler<OrderCreated>
{
    private readonly INotificationService _notificationService;

    public OrderCreatedHandler(INotificationService notificationService)
    {
        _notificationService = notificationService;
    }

    public async Task Handle(
        OrderCreated notification,
        CancellationToken cancellationToken)
    {
        await _notificationService.SendOrderCreatedEmail(
            notification.OrderId,
            cancellationToken);
    }
}
```

And then you simply publish a message using either `IMediator` or `IPublisher`.
I prefer using the `IPublisher` because it's more expressive:

```csharp
await publisher.Publish(new OrderCreated(order.Id), cancellationToken);
```

MediatR will invoke all the respective handlers.

## Introducing Notification Publisher Strategies

Before MediatR v12, the publishing strategy would invoke each handler individually.

However, there's a new interface `INotificationPublisher` controlling how the handlers are called.

The default implementation of this interface is `ForeachAwaitPublisher`:

```csharp
public class ForeachAwaitPublisher : INotificationPublisher
{
    public async Task Publish(
        IEnumerable<NotificationHandlerExecutor> handlerExecutors,
        INotification notification,
        CancellationToken cancellationToken)
    {
        foreach (var handler in handlerExecutors)
        {
            await handler
                .HandlerCallback(notification, cancellationToken)
                .ConfigureAwait(false);
        }
    }
}
```

But now you can also use the `TaskWhenAllPublisher`:

```csharp
public class TaskWhenAllPublisher : INotificationPublisher
{
    public Task Publish(
        IEnumerable<NotificationHandlerExecutor> handlerExecutors,
        INotification notification,
        CancellationToken cancellationToken)
    {
        var tasks = handlerExecutors
            .Select(handler => handler.HandlerCallback(
                notification,
                cancellationToken))
            .ToArray();

        return Task.WhenAll(tasks);
    }
}
```

Here's a comparison between these two strategies.

`ForeachAwaitPublisher`:

- Invokes each handler one by one
- Fails when an exception occurs in one of the handlers

`TaskWhenAllPublisher`:

- Invokes all the handlers at the same time
- Executes all the handlers regardless of one of them throwing an exception

If you store the task returned by `TaskWhenAllPublisher` you can access the `Task.Exception` property, which will contain an `AggregateException` instance.
You can then implement more robust exception handling.

## Configuring MediatR Notification Publishing Strategy

How do we configure which `INotificationPublisher` strategy MediatR will use?

There's a new way to apply configuration options when calling the `AddMediatR` method.

You supply an `Action<MediatRServiceConfiguration>` delegate and configure the `MediatRServiceConfiguration` instance.

If you want to use the `TaskWhenAllPublisher` strategy, you can either:

- Provide a value for the `NotificationPublisher` property
- Specify the strategy type on the `NotificationPublisherType` property

```csharp
services.AddMediatR(config => {
    config.RegisterServicesFromAssemblyContaining<Program>();

    // Setting the publisher directly will make the instance a Singleton.
    config.NotificationPublisher = new TaskWhenAllPublisher();

    // Seting the publisher type will:
    // 1. Override the value set on NotificationPublisher
    // 2. Use the service lifetime from the ServiceLifetime property below
    config.NotificationPublisherType = typeof(TaskWhenAllPublisher);

    config.ServiceLifetime = ServiceLifetime.Transient;
});
```

You can also implement a custom `INotificationPublisher` instance and use your own implementation instead.

## How Is This Useful?

Being able to **run notification handlers in parallel** provides a significant **performance improvement** over the default behavior.

However, note that all handlers will use the same service scope.

If you have service instances that don't support concurrent access you may run into problems.

Unfortunately, one such service instance is the **EF Core** `DbContext`.

In any case, I think this is a great addition to the already amazing **MediatR** library.

That's all for today.

See you next week.

---

## Frequently asked questions

### How do you publish MediatR notifications in parallel?

MediatR v12 added the INotificationPublisher interface to control how notification handlers are invoked. Configure the TaskWhenAllPublisher strategy in AddMediatR, either by setting the NotificationPublisher property to an instance or NotificationPublisherType to the type, and the handlers run with Task.WhenAll.

### What is the default notification publishing strategy in MediatR?

The default implementation is ForeachAwaitPublisher, which awaits each notification handler one by one and fails when an exception occurs in one of the handlers. Before MediatR v12 this serial behavior was the only option.

### What is the difference between ForeachAwaitPublisher and TaskWhenAllPublisher?

ForeachAwaitPublisher invokes each handler one by one and fails when one throws. TaskWhenAllPublisher invokes all handlers at the same time and executes every handler regardless of exceptions; the returned task exposes an AggregateException you can use for more robust error handling.

### Are parallel MediatR notification handlers safe with EF Core?

Be careful. All handlers share the same service scope, so services that do not support concurrent access can cause problems. The EF Core DbContext is one such service, so handlers that share a DbContext can run into problems in parallel.

### How does publish-subscribe work with MediatR?

You create a message class implementing INotification, write one or more INotificationHandler implementations for it, and publish the message with IMediator or IPublisher. MediatR then invokes all the handlers registered for that notification type.
