Skip to content
Ben McCallum edited this page May 9, 2019 · 68 revisions

A low-ambition library trying to solve a simple problem - decoupling the in-proc sending of messages from handling messages. Cross-platform, supporting .NET 4.5, netstandard1.3, and netstandard2.0.

Setting up

Install via NuGet first: Install-Package MediatR

MediatR has no dependencies. You will need to configure a single factory delegate, used to instantiate all handlers, pipeline behaviors, and pre/post-processors.

You'll need to configure two dependencies: first, the mediator itself. StructureMap example: cfg.For<IMediator>().Use<Mediator>(). The other dependency is the factory delegate, ServiceFactory. The Mediator class is defined as:

public class Mediator : IMediator
{
    public Mediator(
        ServiceFactory serviceFactory)

The factory delegates are named delegates around a couple of generic factory methods:

public delegate object ServiceFactory(Type serviceType);

StructureMap example: cfg.For<ServiceFactory>().Use<ServiceFactory>(ctx => ctx.GetInstance);. This factory delegate is how MediatR builds instances of the request and notification handlers.

Finally, you'll need to register your handlers in your container of choice. StructureMap example:

new Container(cfg => cfg.Scan(scanner => {
    scanner.TheCallingAssembly();
    scanner.AddAllTypesOf(typeof(IRequestHandler<,>));
    scanner.AddAllTypesOf(typeof(INotificationHandler<>));
});

Declare whatever flavor of handler you need - sync, async or cancellable async. From the IMediator side, the interface is async-only, designed for modern hosts.

StructureMap

The full StructureMap example looks like:

var container = new Container(cfg =>
{
    cfg.Scan(scanner =>
    {
        scanner.AssemblyContainingType<Ping>(); // Our assembly with requests & handlers
        scanner.ConnectImplementationsToTypesClosing(typeof(IRequestHandler<,>));
        scanner.ConnectImplementationsToTypesClosing(typeof(INotificationHandler<>));
    });
    cfg.For<ServiceFactory>().Use<ServiceFactory>(ctx => ctx.GetInstance);
    cfg.For<IMediator>().Use<Mediator>();
});

Autofac

The full Autofac example looks like:

// uncomment to enable polymorphic dispatching of requests, but note that
// this will conflict with generic pipeline behaviors
// builder.RegisterSource(new ContravariantRegistrationSource());

// mediator itself
builder
  .RegisterType<Mediator>()
  .As<IMediator>()
  .InstancePerLifetimeScope();

// request & notification handlers
builder.Register<ServiceFactory>(context =>
{
    var c = context.Resolve<IComponentContext>();
    return t => c.Resolve(t);
});


// finally register our custom code (individually, or via assembly scanning)
// - requests & handlers as transient, i.e. InstancePerDependency()
// - pre/post-processors as scoped/per-request, i.e. InstancePerLifetimeScope()
// - behaviors as transient, i.e. InstancePerDependency()
builder.RegisterAssemblyTypes(typeof(MyType).GetTypeInfo().Assembly).AsImplementedInterfaces(); // via assembly scan
//builder.RegisterType<MyHandler>().AsImplementedInterfaces().InstancePerDependency();          // or individually

ASP.NET Core

If you're using ASP.NET Core then you can skip the above configuration and use MediatR's MediatR.Extensions.Microsoft.DependencyInjection package which includes a IServiceCollection.AddMediatR(Assembly) extension method, allowing you to register all Handlers and Pre/PostProcessors in a given assembly.

Other

For more examples, check out the samples for working examples using:

  • Autofac
  • LightInject
  • Castle Windsor
  • DryIoc
  • Ninject
  • Simple Injector
  • StructureMap
  • Lamar
  • Unity

These examples highlight all the features of MediatR including sync/async, request/response, pub/sub and more.

Basics

MediatR has two kinds of messages it dispatches:

  • Request/response messages, dispatched to a single handler
  • Notification messages, dispatched to multiple handlers

Request/response

The request/response interface handles both command and query scenarios. First, create a message:

public class Ping : IRequest<string> { }

Next, create handler:

public class PingHandler : IRequestHandler<Ping, string> {
    public Task<string> Handle(Ping request, CancellationToken cancellationToken) {
        return Task.FromResult("Pong");
    }
}

Finally, send a message through the mediator:

var response = await mediator.Send(new Ping());
Debug.WriteLine(response); // "Pong"

In the case your message does not require a response, use the AsyncRequestHandler<TRequest> base class:

public class OneWay : IRequest { }
public class OneWayHandlerWithBaseClass : AsyncRequestHandler<OneWay> {
    protected override Task Handle(OneWay request, CancellationToken cancellationToken) {
        // Twiddle thumbs
    }
}

Or if the request is completely synchronous, inherit from the base RequestHandler class:

public class SyncHandler : RequestHandler<Ping, string> {
    protected override string Handle(Ping request) {
        return "Pong";
    }
}

Request types

There are two flavors of requests in MediatR - ones that return a value, and ones that do not:

  • IRequest<T> - the request returns a value
  • IRequest - the request does not return a value

To simplify the execution pipeline, IRequest inherits IRequest<Unit> where Unit represents a terminal/ignored return type.

Each request type has its own handler interface, as well as some helper base classes:

  • IRequestHandler<T, U> - implement this and return Task<U>
  • RequestHandler<T, U> - inherit this and return U

Then for requests without return values:

  • IRequestHandler<T> - implement this and you will return Task<Unit>.
  • AsyncRequestHandler<T> - inherit this and you will return Task.
  • RequestHandler<T> - inherit this and you will return nothing (void).

Publishing

For notifications, first create your notification message:

public class Ping : INotification { }

Next, create zero or more handlers for your notification:

public class Pong1 : INotificationHandler<Ping> {
    public Task Handle(Ping notification, CancellationToken cancellationToken) {
        Debug.WriteLine("Pong 1");
        return Task.CompletedTask;
    }
}
public class Pong2 : INotificationHandler<Ping> {
    public Task Handle(Ping notification, CancellationToken cancellationToken) {
        Debug.WriteLine("Pong 2");
        return Task.CompletedTask;
    }
}

Finally, publish your message via the mediator:

await mediator.Publish(new Ping());

Publish strategies

The default implementation of Publish loops through the notification handlers and awaits each one. This ensures each handler is run after one another.

Depending on your use-case for publishing notifications, you might need a different strategy for handling the notifications. Maybe you want to publish all notifications in parallel, or wrap each notification handler with your own exception handling logic.

A few example implementations can be found in MediatR.Examples.PublishStrategies. This shows four different strategies documented on the PublishStrategy enum.

Polymorphic dispatch

Handler interfaces are contravariant:

public interface IRequestHandler<in TRequest, TResponse>
    where TRequest : IRequest<TResponse> {
    Task<TResponse> Handle(TRequest message, CancellationToken cancellationToken);
}
public interface INotificationHandler<in TNotification> {
    Task Handle(TNotification notification, CancellationToken cancellationToken);
}

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

Async

Send/publish are async from the IMediator side, with corresponding sync and async-based interfaces/base classes for requests/responses/notification handlers.

Your handlers can use the async/await keywords as long as the work is awaitable:

public class PingHandler : IRequestHandler<Ping, Pong> {
    public async Task<Pong> Handle(Ping request, CancellationToken cancellationToken) {
        await DoPong(); // Whatever DoPong does
    }
}

You will also need to register these handlers with your container of your choice, similar to the synchronous handlers shown above.