MediatR 是參考中介者模式實現的一個 .NET 工具類庫,支持在進程內以單播或多播的形式進行消息傳遞,通過使用 MediatR
可實現消息的發送和處理充分解耦。
在介紹 MediatR
之前,先簡單瞭解下中介者模式。中介者模式主要是指定義一箇中介對象來調度一系列對象之間的交互關係,各對象之間不需要顯式的相互引用,降低耦合性。如下對比圖(普通模式與中介者模式的區別):
實際上從 MediatR
源代碼中可以看出,它本身也並非標準中介者模式的實現,所以這裏簡單瞭解即可。接下來將先介紹 MediatR
的兩種消息傳遞方式的使用方式,然後再分析其具體實現。
創建一個 .NET Core Web API 項目並安裝 MediatR.Extensions.Microsoft.DependencyInjection
NuGet 包(已含 MediatR
NuGet 包),然後在 ConfigureServices
中註冊服務。
// 掃描 Startup 所在程序集內實現了 Handler 的對象並添加到 IoC 容器中
services.AddMediatR(typeof(Startup));
可通過查看 MediatR.Extensions.Microsoft.DependencyInjection 說明了解 AddMediatR
具體包含了哪些服務的註冊以及各註冊對象的生命週期,基本通過以上一行代碼就已經把 MediatR
相關的服務全部註冊到 IoC 容器中。
單播消息傳遞
單播消息傳遞主要涉及 IRequest
(消息類型) 和 IRequestHandler
(消息處理) 兩個接口。
定義接口 IRequest
的實現類,string
指定消息處理方法的返回值類型,如下:
public class GenericRequest : IRequest<string>
{
public string Name { get; set; }
}
定義接口 IRequestHandler
的實現類,GenericRequest
指定此 Handler
要處理的消息類型,string
指定消息處理方法的返回值類型(與 IRequest
指定的泛型類型一致),另外需實現 Handle
方法,如下:
public class GenericRequestHandler : IRequestHandler<GenericRequest, string>
{
public Task<string> Handle(GenericRequest request, CancellationToken cancellationToken)
{
return Task.FromResult($"This is {request.Name}");
}
}
在 Controller 中進行調用測試:
private readonly IMediator _mediator;
public MediatorController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet]
public async Task<string> GenericRequest()
{
var result = await _mediator.Send(new GenericRequest
{
Name = "GenericRequest"
});
return result;
}
另外針對不同的代碼實現方式,有其他的 request-types 可選,本質上還是基於 IRequest
和 IRequestHandler
的擴展。
多播消息傳遞
多播消息傳遞主要涉及 INotification
(消息類型) 和 INotificationHandler
(消息處理) 兩個接口,另外多播消息傳遞是無返回值的。
定義接口 INotification
的實現類,如下:
public class GenericNotification : INotification
{
public string Name { get; set; }
}
定義接口 INotificationHandler
的實現類,GenericNotification
指定此 Handler
要處理的消息類型,另外需實現 Handle
方法,這裏將爲此消息類型定義兩個 NotificationHandler
實現類,如下:
public class GenericANotificationHandler : INotificationHandler<GenericNotification>
{
public Task Handle(GenericNotification notification, CancellationToken cancellationToken)
{
Console.WriteLine($"A {notification.Name}");
return Task.CompletedTask;
}
}
public class GenericBNotificationHandler : INotificationHandler<GenericNotification>
{
public Task Handle(GenericNotification notification, CancellationToken cancellationToken)
{
Console.WriteLine($"B {notification.Name}");
return Task.CompletedTask;
}
}
在 Controller 中進行調用測試:
[HttpGet]
public async Task GenericNotification()
{
await _mediator.Publish(new GenericNotification
{
Name = "GenericNotification"
});
}
原理分析
建議閱讀下源碼,代碼量少且結構清晰,基本理解沒什麼難度
通過前面的介紹可以瞭解在 MediatR
中面向開發者的核心接口主要是 IRequest
&IRequestHandler
、INotification
&INotificationHandler
、IMediator
。
如下 IMediator
的實現類 Mediator
中的定義:
public class Mediator : IMediator
{
private readonly ServiceFactory _serviceFactory;
private static readonly ConcurrentDictionary<Type, object> _requestHandlers = new ConcurrentDictionary<Type, object>();
private static readonly ConcurrentDictionary<Type, NotificationHandlerWrapper> _notificationHandlers = new ConcurrentDictionary<Type, NotificationHandlerWrapper>();
}
首先定義了 ServiceFactory
對象,它代表當前應用程序的 IoC 容器,在應用初始化階段進行了注入,如 MediatR.Extensions.Microsoft.DependencyInjection
已包含了對應的 ServiceFactory 註冊。由於 ServiceFactory
可自定義,所以開發中也完全可以選擇其他的含 IoC 容器功能的框架,如 Autofac
、Castle Windsor
、DryIoc
等。
另外定義 _requestHandlers
和 _notificationHandlers
分別保存單播和多播消息對象類型對應的 HandlerWrapper
對象,HandlerWrapper
的主要是對 ServiceFactory
對象的傳遞,最終通過 ServiceFactory
從 IoC 容器中獲取對應消息類型的 Handler
對象。
MeidatR
還支持爲單播消息定義消息處理的 Pipeline
,如通過實現 IRequestPreProcessor
、IRequestPostProcessor
在消息處理前後自定義處理行爲,通過實現 IRequestExceptionHandler
、IRequestExceptionAction
在異常時自定義處理行爲,這些實現類也是通過 ServiceFactory
從 IoC 容器中獲取。
以下是單播消息處理的核心代碼:
public override Task<TResponse> Handle(IRequest<TResponse> request, CancellationToken cancellationToken, ServiceFactory serviceFactory)
{
Task<TResponse> Handler() => GetHandler<IRequestHandler<TRequest, TResponse>>(serviceFactory).Handle((TRequest) request, cancellationToken);
return serviceFactory
.GetInstances<IPipelineBehavior<TRequest, TResponse>>()
.Reverse()
.Aggregate((RequestHandlerDelegate<TResponse>) Handler, (next, pipeline) => () => pipeline.Handle((TRequest)request, cancellationToken, next))();
}
首先從 ServiceFactory
獲取 IPipelineBehavior
,然後通 Linq 的 Reverse 方法進行順序顛倒,最後通過 Aggregate 進行委託傳遞並執行,所以最終執行順序是 RequestPreProcessorBehavior
→ Handler
→ RequestPostProcessorBehavior
,這裏的實現可能較難理解,核心是 Aggregate 的使用。
總結
MediatR
在實現上核心是通過保存消息請求對象與消息處理對象的關係,配合 IoC 容器實現的消息傳遞解耦。在實際應用中,通過 MediatR
多播消息傳遞可以使代碼實現邏輯上更加簡潔,另外也有較多的文章介紹了通過 MediatR
實現 CQRS
、EventBus
等。