阅读本文你的收获
- 学习MediatR工具,实现进程内消息发送和处理过程的解耦
- 学习MediatR的两种消息处理模式
- 了解中介者模式和其好处
一、什么是MediatR?
MediatR是一款基于中介者模式的思想
而实现的.NET库,支持.NET Framework和跨平台 的.NET Core。主要是为了解决进程内消息发送与消息处理过程之间的耦合问题。MediatR的作者是Jimmy Bogard,如果你不知道这个人,想必你也用过他开发的AutoMapper吧。
它通过一种进程内消息传递机制(无其他外部依赖),进行请求/响应、命令、查询、通知和事件的消息传递,并通过泛型来支持消息的智能调度。
MediatR有两种消息处理模式:
- Request/Response模式:请求响应模式,一对一,消息(Message)将被单个处理者(Handler)处理,可以有返回值
- Notifictaion模式:发布订阅模式,一对多,Message可以被多个Handler处理,无返回值
二、中介者模式介绍
为什么使用中介者模式?
在现实生活中,中介者的存在是不可缺少的,比如房屋中介、招聘平台等;网络世界中有很多中介者模式的身影,例如QQ游戏平台,聊天室、QQ群和短信平台。
在软件设计领域,为什么要使用中介者模式呢?如果不使用中介者模式的话,各个同事对象将会相互进行引用,如果每个对象都与多个对象进行交互时,将会形成如下图所示的网状结构。
从上图可以发现,如果不使用中介者模式的话,每个对象之间过度耦合,这样的既不利于类的复用也不利于扩展。如果引入了中介者模式,那么对象之间的关系将变成星型结构,采用中介者模式之后会形成如下图所示的结构:
中介者模式使之前的网状结构,现在变成了以中介者为中心的星星结构。这样的设计大大减少了系统的耦合度。
中介者就像一个容器的,它自己把控着整个流程,和每一个对象都有或多或少,或近或远的联系,多个对象之间不用理睬其他对象发生了什么,只是负责自己的模块就好,然后把消息发给中介者,让中介者再分发给其他的具体对象,从而实现通讯 —— 这个思想就是中介者的核心思想,而且也是DDD领域驱动设计的核心思想之一。
中介者模式是23种设计模式之一?
中介者模式是23种设计模式的其中一个。中介者模式是一个行为设计模式,它允许我们公开一个统一的接口,系统的 不同部分 可以通过该接口进行 通信,而 不需要 显式的相互作用; 类图结构如下:
中介者使各个对象不需要显式地相互引用,从而使其耦合性降低,而且可以独立地改变它们之间的交互。
以下情况下可考虑使用中介者模式:
- 一组定义良好的对象之间需要进行通信的场合
- 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。
以下是一个具体例子,联合国就是一个中介者:
三、MediatR的应用场景
以下是 MediatR 的一些适用场景:
- CQRS(Command and Query Responsibility Segregation,命令查询责任分离):MediatR 可以用于实现 CQRS 架构的命令与查询分离。通过将命令和查询封装为不同的请求对象,并使用中介者模式来处理这些请求,可以更好地组织和管理复杂的业务逻辑。
- 事件驱动架构(Event-Driven Architecture):MediatR 可以用于实现事件的发布和订阅模式。通过定义和处理事件通知,可以实现松耦合的组件间通信,以及更灵活的系统扩展和异步处理。
- 插件化和扩展性:MediatR 可以用于实现插件化和可扩展的应用程序架构。通过定义通用的请求和处理逻辑,并利用中介者模式将请求和处理解耦,可以方便地添加、移除和替换各种功能模块。
- 视图模型更新(View Model Updates):MediatR 可以用于处理视图模型的更新操作。通过定义更新请求和相应的处理器,可以实现对视图模型的增、删、改等操作,并在更新完成后及时通知相关组件进行界面更新。
- 领域事件和领域命令:MediatR 可以用于处理领域事件和领域命令。通过定义相应的事件和命令,并使用中介者模式进行处理,可以有效地组织和管理领域逻辑,并实现解耦、可测试和可扩展的领域模型。
总体而言,MediatR适用于需要解耦请求和处理逻辑的场景,能够提高代码的可读性、可维护性和可扩展性。它可以与其他架构模式(如CQRS、事件驱动架构等)结合使用,以满足不同的业务需求和系统设计要求。
四、MediatR使用入门
开发环境:
平台版本是:.NET6
开发框架:ASP.NET Core WebApi
开发工具:Visual Studio 2022
- 引用MediatR的NuGet包,案例版本为11.0.0
3.1 演示 request/response 请求响应模式
此案例,演示一个游戏管理模块的“添加游戏”这个功能。
- 步骤一:创建一个“”添加游戏”请求消息类,需要实现IRequest,或IRequest< T > 接口
//引用命名空间
using MediatR;namespace MediatRWebApp.Requests
{//Request类,string是处理者响应的数据类型public class AddGameRequest : IRequest<string>{public int GameId { get; set; } public string GameName { get; set; }public string GameType { get; set; }}
}
- 步骤二:创建一个消息处理器对象,需实现 IRequestHandler<AddGameRequest , string>接口
using MediatR;
using MediatRWebApp.Requests;namespace MediatRWebApp.RequestHandlers
{//Handler类型,PingRequest是其处理的请求类型,string是处理者响应的数据类型public class AddGameRequestHandler : IRequestHandler<AddGameRequest, string>{/// <summary>/// 处理AddGameRequest请求,返回string类型的响应/// </summary>/// <param name="request">请求对象</param>/// <param name="cancellationToken">取消令牌</param>/// <returns></returns>public Task<string> Handle(AddGameRequest request, CancellationToken cancellationToken){//做如下的一些处理:(代码略)//验证输入的参数是否正确//可能还要做名称的唯一性判断//根据Request来创建一个实体对象//用仓储把他保存到数据库中return Task.FromResult("添加游戏成功!");}}
}
步骤三:在服务容器中注册AddGameRequestHandler
using MediatR;
using MediatRWebApp.RequestHandlers;//在Program.cs 注册AddGameRequestHandler
builder.Services.AddMediatR(typeof(AddGameRequestHandler ));
步骤四:在控制器方法中用Mediator的Send方法发送请求,对应的Request请求将由注册过的Handler来处理。
//在控制器中使用
using MediatR;
using MediatRWebApp.Requests;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;namespace MediatRWebApp.Controllers
{[Route("api/[controller]")][ApiController]public class GameController : ControllerBase{//构造函数注入Mediatorprivate readonly IMediator _mediator;//构造方法public GameController(IMediator mediator){_mediator = mediator;}/// <summary>/// 添加游戏的接口方法/// </summary>/// <returns></returns>[HttpPost]public async Task<IActionResult> Add([FromBody]AddGameDto input){AddGameRequest request = new AddGameRequest (){GameName = input.GameName,GameType = input.GameType };var response = await _mediator.Send(request);return Ok(response);}}
}
3.2 演示 Notification 发布/订阅模式:
- 步骤一:创建一个通知对象,必须继承INotification接口
using MediatR;public class DomainNotification: INotification
{public DomainNotification(string message){Message = message;}public string Message { get; set; } //通知的消息
}
- 步骤二:创建两个消息处理器
//步骤二:创建一个消息处理器
public class DomainNotificationHandler : INotificationHandler<DomainNotification>
{/// <summary>/// 处理消息/// </summary>/// <param name="notification"></param>/// <param name="cancellationToken"></param>/// <returns></returns>public Task Handle(DomainNotification notification, CancellationToken cancellationToken){Console.WriteLine("{0} 被消息处理器DomainNotificationHandler处理了!!", notification.Message);return Task.CompletedTask;}
}//步骤二:创建另一个消息处理器
public class AnotherNotificationHandler : INotificationHandler<DomainNotification>
{/// <summary>/// 处理消息/// </summary>/// <param name="notification"></param>/// <param name="cancellationToken"></param>/// <returns></returns>public Task Handle(DomainNotification notification, CancellationToken cancellationToken){Console.WriteLine("{0} 被消息处理器AnotherNotificationHandler处理了!!", notification.Message);return Task.CompletedTask;}
}
- 步骤三:通过 中介者对象 发布通知消息(发布订阅模式)
using MediatR;
using MediatRWebApp.Notifications;
using MediatRWebApp.Requests;[Route("api/[controller]")][ApiController]public class PingController : ControllerBase{private readonly IMediator _mediator;//构造方法public PingController (IMediator mediator){_mediator = mediator;}/// <summary>/// 测试方法/// </summary>/// <returns></returns>[HttpPost]public async Task<IActionResult> TestNotification(){//发送请求消息 PingRequestvar response = await _mediator.Send(new PingRequest());//将通知消息广播出去,订阅了DomainNotification的Handler都能够响应await _mediator.Publish(new DomainNotification("添加成功"));return Ok(response);}}
本文到此结束,欢迎各位的支持和鼓励,如果对你有帮助的话,请点赞+关注,或者转发给需要的朋友。