.NET Core 消息傳遞:MediatR

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 可選,本質上還是基於 IRequestIRequestHandler 的擴展。

多播消息傳遞

多播消息傳遞主要涉及 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&IRequestHandlerINotification&INotificationHandlerIMediator

如下 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 容器功能的框架,如 AutofacCastle WindsorDryIoc 等。

另外定義 _requestHandlers_notificationHandlers 分別保存單播和多播消息對象類型對應的 HandlerWrapper 對象,HandlerWrapper 的主要是對 ServiceFactory 對象的傳遞,最終通過 ServiceFactory 從 IoC 容器中獲取對應消息類型的 Handler 對象。

MeidatR 還支持爲單播消息定義消息處理的 Pipeline,如通過實現 IRequestPreProcessorIRequestPostProcessor 在消息處理前後自定義處理行爲,通過實現 IRequestExceptionHandlerIRequestExceptionAction 在異常時自定義處理行爲,這些實現類也是通過 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 進行委託傳遞並執行,所以最終執行順序是 RequestPreProcessorBehaviorHandlerRequestPostProcessorBehavior,這裏的實現可能較難理解,核心是 Aggregate 的使用。

總結

MediatR 在實現上核心是通過保存消息請求對象與消息處理對象的關係,配合 IoC 容器實現的消息傳遞解耦。在實際應用中,通過 MediatR 多播消息傳遞可以使代碼實現邏輯上更加簡潔,另外也有較多的文章介紹了通過 MediatR 實現 CQRSEventBus 等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章