Skip to main content
Version: 2.4.1

MediatR

RCommon integrates with MediatR to route events through the MediatR notification pipeline. Each event is wrapped in a MediatRNotification<TEvent> and published to all registered INotificationHandler instances. From the application's perspective, handlers still implement ISubscriber<TEvent> — the MediatR plumbing is hidden behind the abstraction.

Installation​

NuGet Package
dotnet add package RCommon.MediatR

Configuration​

Use WithEventHandling<MediatREventHandlingBuilder> on the RCommon builder. Register a producer and one subscriber per event type:

using RCommon;
using RCommon.MediatR;
using RCommon.MediatR.Producers;

builder.Services.AddRCommon()
.WithEventHandling<MediatREventHandlingBuilder>(eventHandling =>
{
eventHandling.AddProducer<PublishWithMediatREventProducer>();
eventHandling.AddSubscriber<OrderPlaced, OrderPlacedHandler>();
eventHandling.AddSubscriber<OrderShipped, OrderShippedHandler>();
});

AddSubscriber does three things internally:

  1. Registers ISubscriber<TEvent> with the DI container.
  2. Registers a MediatREventHandler<TEvent, MediatRNotification<TEvent>> as an INotificationHandler so MediatR can dispatch the notification.
  3. Records the event-to-producer association in the EventSubscriptionManager so the router routes this event only to the MediatR producer.

Custom MediatR service configuration​

If you need to control which assemblies MediatR scans for handlers, use the three-parameter overload:

builder.Services.AddRCommon()
.WithEventHandling<MediatREventHandlingBuilder>(
eventHandling =>
{
eventHandling.AddProducer<PublishWithMediatREventProducer>();
eventHandling.AddSubscriber<OrderPlaced, OrderPlacedHandler>();
},
mediatR =>
{
mediatR.RegisterServicesFromAssembly(typeof(Program).Assembly);
});

Defining an event​

Events must implement ISerializableEvent. The ISyncEvent and IAsyncEvent markers control dispatch ordering through the IEventRouter:

using RCommon.Models.Events;

public class OrderPlaced : ISyncEvent
{
public OrderPlaced(Guid orderId, DateTime placedAt)
{
OrderId = orderId;
PlacedAt = placedAt;
}

public OrderPlaced() { }

public Guid OrderId { get; }
public DateTime PlacedAt { get; }
}

Implementing a subscriber​

Implement ISubscriber<TEvent>. MediatR delivery is transparent:

using RCommon.EventHandling.Subscribers;

public class OrderPlacedHandler : ISubscriber<OrderPlaced>
{
private readonly ILogger<OrderPlacedHandler> _logger;

public OrderPlacedHandler(ILogger<OrderPlacedHandler> logger)
{
_logger = logger;
}

public async Task HandleAsync(OrderPlaced @event, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Order {OrderId} placed at {PlacedAt}", @event.OrderId, @event.PlacedAt);
await Task.CompletedTask;
}
}

Publishing events​

Produce events through IEventRouter or IEventProducer. The producer wraps each event in a MediatRNotification<TEvent> and calls IMediatorService.Publish, which dispatches to all registered notification handlers:

public class OrderService
{
private readonly IEventRouter _eventRouter;

public OrderService(IEventRouter eventRouter)
{
_eventRouter = eventRouter;
}

public async Task PlaceOrderAsync(Order order, CancellationToken cancellationToken)
{
// ... persist the order ...

_eventRouter.AddTransactionalEvent(new OrderPlaced(order.Id, DateTime.UtcNow));
await _eventRouter.RouteEventsAsync(cancellationToken);
}
}

How MediatR is wired​

Internally, PublishWithMediatREventProducer calls IMediatorService.Publish(@event, cancellationToken). The mediator service wraps the event in a MediatRNotification<TEvent> and dispatches it through MediatR's notification pipeline. MediatREventHandler<TEvent, TNotification> receives the notification and resolves ISubscriber<TEvent> from the DI container to call HandleAsync.

This means MediatR pipeline behaviours (logging, validation, unit-of-work) apply to event handlers just as they apply to command and query handlers, giving you a consistent cross-cutting-concerns story.

API summary​

TypeDescription
MediatREventHandlingBuilderBuilder used with WithEventHandling<T> to configure MediatR event handling
IMediatREventHandlingBuilderInterface for the MediatR event handling builder
PublishWithMediatREventProducerIEventProducer that publishes via IMediatorService.Publish (fan-out)
SendWithMediatREventProducerIEventProducer that sends via IMediatorService.Send (point-to-point)
MediatREventHandler<TEvent, TNotification>Internal INotificationHandler that bridges MediatR to ISubscriber<TEvent>
MediatRNotification<TEvent>MediatR INotification wrapper used to carry events through the pipeline
RCommonRCommon