Behaviors

Michael Crawford edited this page Feb 7, 2018 · 7 revisions

New in MediatR 3.0 are behaviors, which allow you to build your own pipeline directly inside of MediatR without resolving to using decorators around your handlers. It's a more natural way to enhance your handlers with behavior and better supported in containers.

A pipeline behavior is an implementation of IPipelineBehavior<TRequest, TResponse>. It represents a similar pattern to filters in ASP.NET MVC/Web API or pipeline behaviors in NServiceBus. Your pipeline behavior needs to implement one method:

Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next);

The request parameter is the request object passed in through IMediator.Send, while the next parameter is an async continuation for the next action in the behavior chain. In your behavior, you'll need to await or return the invocation of the next delegate. A simple example:

public class LoggingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> {
    public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) {
        await Logger.InfoAsync($"Handling {typeof(TRequest).Name}");
        var response = await next();
        await Logger.InfoAsync($"Handled {typeof(TResponse).Name}");
        return response;
    }
}

Because the next delegate does not accept the TRequest as a parameter, you can mutate the incoming request but cannot replace it.

NB

The pipeline behaviors are only compatible with IRequestHandler<TRequest,TResponse> and can't be used with INotificationHandler<TRequest>.

Registering pipeline behaviors

Most containers support registering multiple instances of a type. StructureMap for example will allow you to register open generic (and constrained) implementations:

cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(OuterBehavior<,>));
cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(InnerBehavior<,>));
cfg.For(typeof(IPipelineBehavior<,>)).Add(typeof(ConstrainedBehavior<,>));

Just register the behaviors in the order you would like them to be called. MediatR uses the MultiInstanceFactory delegate to resolve the IEnumerable<IPipelineBehavior<TRequest, TResponse>> for whatever closed types your request and responses are. For void/Task requests, the TResponse type will be Unit.

Autofac Limitations

If you add the ContravariantRegistrationSource to Autofac to enable polymorphic dispatch, you cannot simultaneously use open generic registrations. Attempting to do so will result in your pipeline behaviors being called multiple times, once for each type in the inheritance hierarchy of TRequest. See this issue for details and possible workarounds.

Built-in behaviors

To ease development, two built-in behaviors exist:

  • RequestPreProcessorBehavior will execute IRequestPreProcessor implementations before any handlers are called
  • RequestPostProcessorBehavior will execute IRequestPostProcessor implementations after all handlers are called

You can find these in the MediatR.Pipeline namespace, and will need to be explicitly registered with your container as open generics.

Additionally, any IRequestPreProcessor and IRequestPostProcessor will need to be registered with the container.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.