MediatR[1] 是参考中介者模式实现的一个 .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[2]  说明了解 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[3] 可选,本质上还是基于 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 注册[4]。由于 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 进行委托传递并执行,所以最终执行顺序是 RequestPreProcessorBehavior →  HandlerRequestPostProcessorBehavior,这里的实现可能较难理解,核心是 Aggregate 的使用。

总结

MediatR 在实现上核心是通过保存消息请求对象与消息处理对象的关系,配合 IoC 容器实现的消息传递解耦。在实际应用中,通过 MediatR 多播消息传递可以使代码实现逻辑上更加简洁,另外也有较多的文章介绍了通过  MediatR 实现 CQRSEventBus 等。

参考资料

[1]

MediatR: https://github.com/jbogard/MediatR

[2]

MediatR.Extensions.Microsoft.DependencyInjection: https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection

[3]

request-types: https://github.com/jbogard/MediatR/wiki#request-types

[4]

ServiceFactory 注册: https://github.com/jbogard/MediatR.Extensions.Microsoft.DependencyInjection/blob/master/src/MediatR.Extensions.Microsoft.DependencyInjection/Registration/ServiceRegistrar.cs#L219

ASP.NET Core 消息传递:MediatR相关推荐

  1. Asp.net core使用MediatR进程内发布/订阅

    1.背景 最近,一个工作了一个月的同事离职了,所做的东西怼了过来.一看代码,惨不忍睹,一个方法六七百行,啥也不说了吧,实在没法儿说.介绍下业务场景吧,一个公共操作A,业务中各个地方都会做A操作,正常人 ...

  2. 使用Vue.js和ASP.NET Core MVC实现CQRS模式

    目录 介绍 先决条件 深入了解基本信息 应用解决方案结构 图像上传和显示应用 MVC与JS框架之间的通信设计 在SPA中,在表示层中添加UI和PLL 用于数据读取和写入操作的数据访问层 软件包安装 读 ...

  3. 如何在 ASP.Net Core 中使用 MediatR

    MediatR 是一个 中介者模式 的.NET开源实现, 中介者模式 管控了一组对象之间的相互通讯并有效的减少了对象之间错综复杂的相互依赖,在 中介者模式 中,一个对象不需要直接和另一个对象进行通讯, ...

  4. ASP.NET Core中使用MediatR实现命令和中介者模式

    作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9866068.html 在本文中,我将解释命令模式,以及如何利用基于命令模式的第三方库来实现它们,以及如何 ...

  5. [译]ASP.NET Core中使用MediatR实现命令和中介者模式

    在本文中,我将解释命令模式,以及如何利用基于命令模式的第三方库来实现它们,以及如何在ASP.NET Core中使用它来解决我们的问题并使代码简洁.因此,我们将通过下面的主题来进行相关的讲解. 什么是命 ...

  6. 【翻译】asp.net core中使用MediatR

    这篇文章来自:https://ardalis.com/using-mediatr-in-aspnet-core-apps 本文作为翻译,有一些单词翻译成中文可能会有一些误解(对于读者)或者错误(对于作 ...

  7. 为什么应该在业务层实现管道模式,而不用ASP.NET Core Middleware实现 | 2点原因和实现方式...

    前言 ASP.NET Core的Middleware(中间件)就是使用了管道模式: Request(请求)在管道中传递,依次经过管道中的每一个MiddleWare进行处理. MiddleWare就像一 ...

  8. asp.net core监控—引入Prometheus(二)

    上一篇博文中,说明了怎么引进Prometheus到asp.net core项目中,因为是Demo,所以Prometheus和Grafana都是windows版本,本地执行的,生产环境上这些服务可以根据 ...

  9. ASP.NET Core 3.x启动时运行异步任务(一)

    这是一个大的题目,需要用几篇文章来说清楚.这是第一篇.   一.前言 在我们的项目中,有时候我们需要在应用程序启动前执行一些一次性的逻辑.比方说:验证配置的正确性.填充缓存.或者运行数据库清理/迁移等 ...

最新文章

  1. 【Tools】CMAKE的使用
  2. Spring AOP 切点(pointcut)表达式
  3. 实现oracle-job准确定时
  4. 微信小程序数据拼接_微信小程序用户数据解密算法Java版
  5. des解密不完整,前面几位是乱码的解决办法
  6. 00后社交突围:今天你CDX了吗?
  7. CSS基础学习 18.CSS多列
  8. C# Zip解压缩,规避 [content_types].xml 文件
  9. java中使用QBC的好处_使用QBC的方式应用多对多关系中的查询
  10. ubuntu命令行语法_Linux中重定向命令行的总结(ubuntu学习第三讲)
  11. Echarts数据可视化toolbox工具框,开发全解+完美注释
  12. 某高手毕生精力总结的电脑技巧
  13. 使用GDAL库读取SRTM格式的高程数据
  14. 脉冲计数器单片机c语言编程,基于单片机的光电计数器
  15. 传智黑马java基础学习——day11(接口、多态)
  16. 猜拳游戏 java_用java实现一个猜拳小游戏
  17. Microbiome:40年施肥处理后固氮菌及氮固定受抑制
  18. Vue的引入方式,属性和计数器案例
  19. 交大博士血泪自述:不是读博的料,别上博士这条船
  20. JavaScript检测原始值、引用值、属性

热门文章

  1. java里面的文件上传与下载
  2. Android之EditText自定义边框和边框颜色(转载)
  3. SQL Server数据库同步问题分享[未完,待续](一)
  4. php图片地址参数错误,图片上传时一直显示请求地址错误怎么办
  5. 如何在iPhone上共享视频之前从视频中删除音频
  6. 正在创建系统还原点_如何使Windows在启动时自动创建系统还原点
  7. python中的logger模块详细讲解
  8. weui-react项目实战新心得
  9. 关于质量的联想:消费示范效应
  10. 2016年:勒索病毒造成损失预估超过10亿美元