【.NET后端工具系列】MediatR实现进程内消息通讯

阅读本文你的收获

  1. 学习MediatR工具,实现进程内消息发送和处理过程的解耦
  2. 学习MediatR的两种消息处理模式
  3. 了解中介者模式和其好处

一、什么是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

  1. 引用MediatR的NuGet包,案例版本为11.0.0
    添加MediatR

3.1 演示 request/response 请求响应模式

此案例,演示一个游戏管理模块的“添加游戏”这个功能。

  1. 步骤一:创建一个“”添加游戏”请求消息类,需要实现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; }
     }
}
  1. 步骤二:创建一个消息处理器对象,需实现 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
    {
        //构造函数注入Mediator
        private 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 发布/订阅模式:

  1. 步骤一:创建一个通知对象,必须继承INotification接口
using MediatR;

public class DomainNotification: INotification
{
    public DomainNotification(string message)
    {
        Message = message;
    }

    public string Message { get; set; }  //通知的消息
}
  1. 步骤二:创建两个消息处理器
//步骤二:创建一个消息处理器
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;
    }
}
  1. 步骤三:通过 中介者对象 发布通知消息(发布订阅模式)
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()
     {
         //发送请求消息 PingRequest
         var response = await _mediator.Send(new PingRequest());

         //将通知消息广播出去,订阅了DomainNotification的Handler都能够响应
         await _mediator.Publish(new DomainNotification("添加成功"));

         return Ok(response);
     }
 }

本文到此结束,欢迎各位的支持和鼓励,如果对你有帮助的话,请点赞+关注,或者转发给需要的朋友。

  • 20
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

采石之人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值