Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
Jimmy Bogard edited this pageAug 22, 2025 ·70 revisions

MediatR is a low-ambition library trying to solve a simple problem — decoupling the in-process sending of messages from handling messages. Cross-platform, supportingnetstandard2.0.

Setup

Install the package via NuGet first:Install-Package MediatR

MediatR directly referencesMicrosoft.Extensions.DependencyInjection.Abstractions leveragingIServiceProvider. Typical usage is to useIServiceCollection directly:

services.AddMediatR(cfg=>{cfg.LicenseKey="<License Key here>";cfg.RegisterServicesFromAssembly(typeof(Program).Assembly);});

This method registers the known MediatR types:

  • IMediator as transient
  • ISender as transient
  • IPublisher as transient

For each assembly registered, theAddMediatR method will scan those assemblies for MediatR types (excluding behaviors):

  • IRequestHandler<,> concrete implementations as transient
  • IRequestHandler<> concrete implementations as transient
  • INotificationHandler<> concrete implementations as transient
  • IStreamRequestHandler<> concrete implementations as transient
  • IRequestExceptionHandler<,,> concrete implementations as transient
  • IRequestExceptionAction<,>) concrete implementations as transient

Behaviors and pre/post processors must be registered explicitly through theAddXyz methods.

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:

publicclassPing:IRequest<string>{}

Next, create a handler:

publicclassPingHandler:IRequestHandler<Ping,string>{publicTask<string>Handle(Pingrequest,CancellationTokencancellationToken){returnTask.FromResult("Pong");}}

Finally, send a message through the mediator:

varresponse=awaitmediator.Send(newPing());Debug.WriteLine(response);// "Pong"

In the case your message does not require a response, implement the non-genericIRequest interface and subsequent handler:

publicclassOneWay:IRequest{}publicclassOneWayHandler:IRequestHandler<OneWay>{publicTaskHandle(OneWayrequest,CancellationTokencancellationToken){// do workreturnTask.CompletedTask;}}

Request types

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

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

Each request type has its own handler interface:

  • IRequestHandler<TRequest, TResponse> - implement this and returnTask<TResponse>

Then for requests without return values:

  • IRequestHandler<TRequest> - implement this and you will returnTask.

Streams and AsyncEnumerables

To create a stream from a request, first implement the stream request and its response:

  • IStreamRequest<TResponse>

    publicsealedclassCounterStreamRequest:IStreamRequest<int>{}

Stream request handlers are separate from the normalIRequestHandler and require implementing:

  • IStreamRequestHandler<TRequest, TResponse>

    publicsealedclassCounterStreamHandler:IStreamRequestHandler<CounterStreamRequest,int>{publicasyncIAsyncEnumerable<int>Handle(CounterStreamRequestrequest,[EnumeratorCancellation]CancellationTokencancellationToken){intcount=0;while(!cancellationToken.IsCancellationRequested){awaitTask.Delay(500,cancellationToken);yieldreturncount;count++;}}}

Unlike normal request handlers that return a singleTResponse, a stream handler returns anIAsyncEnumerable<TResponse>:

IAsyncEnumerable<TResponse>Handle(TRequestrequest,CancellationTokencancellationToken);

To create a stream request handler, create a class that implementsIStreamRequestHandler<TRequest, TResponse> and implement the above Handle method.

Finally, send a stream message through the mediator:

CancellationTokenSourcects=new();intcount=10;awaitforeach(variteminmediator.CreateStream<int>(newCounterStreamRequest(),cts.Token)){count--;if(count==0){cts.Cancel();}Debug.WriteLine(item);}

Notifications

For notifications, first create your notification message:

publicclassPing:INotification{}

Next, create zero or more handlers for your notification:

publicclassPong1:INotificationHandler<Ping>{publicTaskHandle(Pingnotification,CancellationTokencancellationToken){Debug.WriteLine("Pong 1");returnTask.CompletedTask;}}publicclassPong2:INotificationHandler<Ping>{publicTaskHandle(Pingnotification,CancellationTokencancellationToken){Debug.WriteLine("Pong 2");returnTask.CompletedTask;}}

Finally, publish your message via the mediator:

awaitmediator.Publish(newPing());

Custom notification publishers

MediatR (starting with version 12) supports custom publish strategies:

publicinterfaceINotificationPublisher{TaskPublish(IEnumerable<NotificationHandlerExecutor>handlerExecutors,INotificationnotification,CancellationTokencancellationToken);}

Custom publish strategies are injected into theMediator class, and are registered withAddMediatR:

services.AddMediatR(cfg => {    cfg.RegisterServicesFromAssemblyContaining<Program>();    cfg.NotificationPublisher = new MyCustomPublisher(); // this will be singleton    cfg.NotificationPublisherType = typeof(MyCustomPublisher); // this will be the ServiceLifetime});

There are two built-in notification publishers:

  • ForeachAwaitPublisher - the default, existing implementation
  • TaskWhenAllPublisher - awaitsTask.WhenAll for all notification handler tasks. Does NOT useTask.Run

Custom notification publishers can also use the handler instance to do custom logic like ordering, skipping handlers, etc. The handler instance is on theNotificationHandlerExecutor class, as well as the delegate to call the handler instance itself.

Auto register Generic Requests and Handlers

Configuration

/// <summary>/// Configure the maximum number of type parameters that a generic request handler can have. To Disable this constraint, set the value to 0./// </summary>publicintMaxGenericTypeParameters{get;set;}=10;/// <summary>/// Configure the maximum number of types that can close a generic request type parameter constraint.  To Disable this constraint, set the value to 0./// </summary>publicintMaxTypesClosing{get;set;}=100;/// <summary>/// Configure the Maximum Amount of Generic RequestHandler Types MediatR will try to register.  To Disable this constraint, set the value to 0./// </summary>publicintMaxGenericTypeRegistrations{get;set;}=125000;/// <summary>/// Configure the Timeout in Milliseconds that the GenericHandler Registration Process will exit with error.  To Disable this constraint, set the value to 0./// </summary>publicintRegistrationTimeout{get;set;}=15000;/// <summary>/// Flag that controlls whether MediatR will attempt to register handlers that containg generic type parameters./// </summary>publicboolRegisterGenericHandlers{get;set;}=false;

Implementation

Requests can be made generic such that work done to or for specific type arguments can be written once towards an abstraction.

//you can also configure other values shown above here as wellbuilder.Services.AddMediatR(cfg=>{cfg.RegisterGenericHandlers=true;cfg.RegisterServicesFromAssembly(typeof(GenericPing<>).Assembly);});
publicinterfaceIPong{string?Message{get;}}publicclassPong:IPong{publicstring?Message{get;set;}}//generic request definitionpublicclassGenericPing<T>:IRequest<T>whereT:class,IPong{publicT?Pong{get;set;}}//generic request handlerpublicclassGenericPingHandler<T>:IRequestHandler<GenericPing<T>,T>whereT:class,IPong{publicTask<T>Handle(GenericPing<T>request,CancellationTokencancellationToken)=>Task.FromResult(request.Pong!);}//usagevarpong=await_mediator.Send(newGenericPing<Pong>{Pong=new(){Message="Ping Pong"}});Console.WriteLine(pong.Message);//would output "Ping Pong"

Notes

  • All assemblies from which your generic handlers, requests, or type parameters must be added to be registered.
  • To use primitives as generic types in requests and handlers you will need to include the assembly in which they are defined I.E.typeof(int).Assembly.

Polymorphic dispatch

Handler interfaces are contravariant:

publicinterfaceIRequestHandler<inTRequest,TResponse>whereTRequest:IRequest<TResponse>{Task<TResponse>Handle(TRequestmessage,CancellationTokencancellationToken);}publicinterfaceINotificationHandler<inTNotification>{TaskHandle(TNotificationnotification,CancellationTokencancellationToken);}

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

Async

Send/publish are asynchronous from theIMediator side, with corresponding synchronous and asynchronous-based interfaces/base classes for requests/responses/notification handlers.

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

publicclassPingHandler:IRequestHandler<Ping,Pong>{publicasyncTask<Pong>Handle(Pingrequest,CancellationTokencancellationToken){awaitDoPong();// Whatever DoPong does}}

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

Exceptions handling

Exception handler pipeline step

Exception handler implemented by usingIPipelineBehavior concept. It requires to add theRequestExceptionProcessorBehavior to the request execution Pipeline. This behavior is not registered unlessAddMediatR finds request exception behaviors.

namespaceMediatR.Pipeline;/// <summary>/// Behavior for executing all <see cref="IRequestExceptionHandler{TRequest,TResponse,TException}"/> instances///     after an exception is thrown by the following pipeline steps/// </summary>/// <typeparam name="TRequest">Request type</typeparam>/// <typeparam name="TResponse">Response type</typeparam>publicclassRequestExceptionProcessorBehavior<TRequest,TResponse,TException>:IRequestExceptionHandler<TRequest,TResponse,TException>whereTRequest:notnull{

Exception action pipeline step

Exception action implemented by usingIPipelineBehavior concept. It requires to add theRequestExceptionActionProcessorBehavior to the request execution Pipeline.If placeRequestExceptionActionProcessorBehavior beforeRequestExceptionProcessorBehavior, actions will be called only for unhandled exceptions.

namespaceMediatR.Pipeline;/// <summary>/// Behavior for executing all <see cref="IRequestExceptionAction{TRequest,TException}"/> instances///     after an exception is thrown by the following pipeline steps/// </summary>/// <typeparam name="TRequest">Request type</typeparam>/// <typeparam name="TResponse">Response type</typeparam>publicclassRequestExceptionActionProcessorBehavior<TRequest,TResponse>:IRequestExceptionAction<TRequest,TException>whereTRequest:notnull{

Handlers and actions priority execution

All available handlers/actions will be sorted by applying next rules:

  • The handler/action has a higher priority if it belongs to the current assembly (same assembly with request) and the other is not. If none of the objects belong to the current assembly, they can be considered equal. If both objects belong to the current assembly, they can't be compared only by this criterion - compare by next rule;
  • The handler/action has a higher priority if it belongs to the current/child request namespace and the other is not. If both objects belong to the current/child request namespace, they can be considered equal. If none of the objects belong to the current/child request namespace, they can't be compared by this criterion - compare by next rule;
  • The handler/action has a higher priority if it namespace is part of the current location (request namespace) and the other is not. If both objects are part of the current location, the closest has higher priority. If none of the objects are part of the current location, they can be considered equal.

Override handler/action

Create a new handler / action that inherits the handler / action that you want to override, and save it in accordance with the priority rules.

Exception handler vs Exception action

  • All actions will be performed for the thrown exception. However, only one handler can handle the thrown exception;
  • The exception will be re-thrown after all actions are completed. But the exception can be handled by the exception handler, and the result will be returned to the caller;
  • The actions execution process faster. Because the exception handler works liketry / catch block with severalcatch and search handlers for all base exceptions separately;
  • Both support priority sorting;
  • Both support overriding.

Adding my own footer here

Clone this wiki locally

[8]ページ先頭

©2009-2025 Movatter.jp