项目中使用了MediatR,这里研究一下,这篇文章是这个专题的第一篇,后续会进行源码分析,这篇先介绍和基本使用。
MediatR 有两种方式的消息发送方式:
- Request / Response (单播消息),指派到 一个 处理程序
- Notification (广播消息),指派到 多个处理程序
我们先看单播消息
单播消息传递主要涉及 IRequest(消息类型) 和 IRequestHandler(消息处理) 两个接口。
使用 MediatR 的 单播消息 的原因就是用命令模式的原因
下面的链接是讲命令模式的
http://c.biancheng.net/view/1380.html
在程序中,“方法的调用者”与“方法的实现者”之间经常存在紧密的耦合关系,这不利于软件功能的扩展与维护。
比如 我自己写的一个例子
接口:
public interface IPeopleService
{
Task<bool> AddPeople();
Task<List<object>> GetPeople();
}
接口实现:
public class PeopleService : IPeopleService
{
public Task<bool> AddPeople()
{
Thread.Sleep(2000);
return Task.FromResult(true);
}
}
控制器:
[Route("api/[controller]")]
[ApiController]
public class PersonController : ControllerBase
{
private readonly IPeopleService peopleService;
public PersonController(IPeopleService peopleService)
{
this.peopleService = peopleService;
}
[HttpPost, Route("AddPeople")]
public async Task<bool> AddPeople()
{
return await peopleService.AddPeople();
}
}
从上面的代码可以看到,调用方法时,控制器和IPeopleService紧耦合了
下面使用MediatR进行改进
先来看下命令模式的定义:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通。减小了耦合
首先 安装 MediatR.Extensions.Microsoft.DependencyInjection 包
然后添加
// 扫描 Startup 所在程序集内实现了 Handler 的对象并添加到 IoC 容器中
services.AddMediatR(typeof(Startup));
- 创建一个命令对象
public class PeopleAddComm : IRequest<bool> //bool是该命令处理后的返回结果的类型
{
}
- 创建命令的实现
//bool是返回结果的类型
public class PeopleAddCommHandler : IRequestHandler<PeopleAddComm, bool>
{
public Task<bool> Handle(PeopleAddComm request, CancellationToken cancellationToken)
{
Thread.Sleep(200);
return Task.FromResult(true);
}
}
- 在控制器中使用
[Route("api/[controller]")]
[ApiController]
public class PersonController : ControllerBase
{
private readonly IMediator _mediator;
public PersonController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost, Route("AddPeople")]
public async Task<bool> AddPeople()
{
PeopleAddComm addComm = new PeopleAddComm();
return await _mediator.Send(addComm);
}
}
和之前的对比,有没有发现 控制器里没有再引入IPeopleService对象了,控制器类和它所调用的方法进行了解耦。
下面来说一下MediatR 的 Notification
多播消息传递涉及 INotification 和 INotificationHandler两个接口,另外多播消息传递是无返回值的。
现在,模拟一个业务场景,用户注册时,要进行以下步骤
- 保存到数据库
- 记录日志
- 发送邮件
public class RegisterNotification : INotification
{
public string LoginName { get; set; }
public string Pwd { get; set; }
}
public class RegisterNotiDbHandler : INotificationHandler<RegisterNotification>
{
public Task Handle(RegisterNotification notification, CancellationToken cancellationToken)
{
//存入数据库
Console.WriteLine($"存入数据库 {notification.LoginName}, {notification.Pwd}");
Thread.Sleep(1000);
return Task.CompletedTask;
}
}
public Task Handle(RegisterNotification notification, CancellationToken cancellationToken)
{
//记录日志
Console.WriteLine($"记录日志=> {notification.LoginName}, {notification.Pwd}");
Thread.Sleep(1000);
return Task.CompletedTask;
}
public class RegisterNotiEmailHandler : INotificationHandler<RegisterNotification>
{
public Task Handle(RegisterNotification notification, CancellationToken cancellationToken)
{
//发送邮件
Console.WriteLine($"发送邮件=> {notification.LoginName}, {notification.Pwd}");
Thread.Sleep(1000);
return Task.CompletedTask;
}
}
控制器的方法:
[Route("api/[controller]")]
[ApiController]
public class PersonController : ControllerBase
{
private readonly IMediator _mediator;
public PersonController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost, Route("Register")]
public void Register(string loginName, string pwd)
{
RegisterNotification registerNotification = new RegisterNotification { LoginName = loginName, Pwd = pwd };
_mediator.Publish(registerNotification);
}
}
调用结果:
方法调用顺序:
经测试是按照类的先后顺序来的
MediatR 中的核心接口主要是 IRequest&IRequestHandler 、INotification&INotificationHandler、IMediator 。
以上就是MediatR的使用,就是这么简单,没有什么复杂的用法,顺便说一下 写MediatR的人也是Automapper的作者。下一篇我们一起看下源码。
源码地址:
https://github.com/jbogard/MediatR