ASP.NET Core 消息传递:MediatR

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308716.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Spring5]IOC容器_Bean管理XML方式_注入其他类型属性

xml注入其他属性 bean: package com.atguigu.spring;/*** 演示使用set方法进行注入属性*/ public class Book {private String bname;private String bauthor;private String address;public Book(String address) {this.address address;}public String getBname() {return…

ERP平台的自动化测试技术实践

源宝导读&#xff1a;ERP是“业务密集”的大型复杂软件&#xff0c;而且对于业务逻辑与数据的精确度要求几乎是零容忍&#xff0c;其质量保障的挑战很大。本文将介绍ERP平台通过自动化测试保障质量的技术实践。一、自动化测试概念介绍测试金字塔原理1.1、测试的成本UI自动化依赖…

[Spring5]IOC容器_Bean管理XML方式_注入集合类型属性

xml注入集合属性 1.注入数组类型属性 2.注入List集合类型属性 3.注入Map集合类型属性 &#xff08;1&#xff09;创建类&#xff0c;定义数组&#xff0c;list&#xff0c;map&#xff0c;set类型属性&#xff0c;生成对应set方法 package com.atguigu.collectiontype;imp…

Logging with ElasticSearch, Kibana, ASP.NET Core and Docker

“好久不见&#xff0c;前两周经历了人生第一次"伪牛市"&#xff0c;基金和股市大起大落&#xff0c;更加坚信“你永远赚不到超出你认知范围之外的钱,除非靠着运气”&#xff0c;老韭菜诚不欺我也。当能力与野心不匹配&#xff0c;只能多看书&#xff0c;收割那些不求…

[Spring5]IOC容器_Bean管理_工厂Bean

IOC操作Bean管理&#xff08;FactoryBean&#xff09; 1.Spring有两种类型bean&#xff0c;一种普通bean&#xff0c;另外一种工厂bean&#xff08;FactoryBean&#xff09; 2.普通bean&#xff1a;在配置文件中定义bean类型就是返回类型 3.工厂bean&#xff1a;在配置文件定…

Redis 6.0 新特性 ACL 介绍

Redis 6.0 新特性 ACL 介绍Intro在 Redis 6.0 中引入了 ACL&#xff08;Access Control List) 的支持&#xff0c;在此前的版本中 Redis 中是没有用户的概念的&#xff0c;其实没有办法很好的控制权限&#xff0c;redis 6.0 开始支持用户&#xff0c;可以给每个用户分配不同的权…

[Spring5]IOC容器_Bean管理_bean的作用域和bean的生命周期

IOC操作Bean管理&#xff08;bean作用域&#xff09; 1.在Spring里面&#xff0c;设置创建bean实例是单实例还是多实例 2.在Spring里面&#xff0c;默认情况下&#xff0c;bean是单实例对象 package com.atguigu.spring.test;import com.atguigu.spring.collectiontype.Book…

手动造轮子——为Ocelot集成Nacos注册中心

前言近期在看博客的时候或者在群里看聊天的时候&#xff0c;发现很多都提到了Ocelot网关的问题。我之前也研究过一点&#xff0c;网关本身是一种通用的解决方案&#xff0c;主要的工作就是拦截请求统一处理&#xff0c;比如认证、授权、熔断、限流、注册发现、负载均衡等等。随…

程序员修神之路--简约而不简单的分布式通信基石

点击“蓝字”关注&#xff0c;领取架构书籍菜菜哥&#xff0c;请教一个问题呗面试又被卡住了&#xff1f;还是你了解我呀&#xff0c;tcp协议面向连接是怎么回事呢&#xff1f;这个说详细起来&#xff0c;那本好几百页的tcp协议的书籍你倒是可以看看分布式系统可以总结为是处于…

[Spring5]IOC容器_Bean管理注解方式_创建对象

IOC操心Bean管理&#xff08;基于注解方式&#xff09; 1.什么是注解 &#xff08;1&#xff09;注解是代码特殊标记&#xff0c;格式&#xff1a;注解名称(属性名称属性值,属性名称属性值…) &#xff08;2&#xff09;使用注解&#xff0c;注解作用在类上面&#xff0c;方…

一个有趣的问题, 你知道SqlDataAdapter中的Fill是怎么实现的吗

一&#xff1a;背景1. 讲故事最近因为各方面原因换了一份工作&#xff0c;去了一家主营物联柜的公司&#xff0c;有意思的是物联柜上的终端是用 wpf 写的&#xff0c;代码也算是年久失修&#xff0c;感觉技术债还是蛮重的&#xff0c;前几天在调试一个bug的时候&#xff0c;看到…

.Net Core in Docker极简入门(上篇)

点击上方蓝字"小黑在哪里"关注我吧环境准备Docker基础概念Docker基础命令Docker命令实践构建Docker镜像Dockerfilebulid & run前言Docker 是一个开源的应用容器引擎&#xff0c;它十分火热&#xff0c;如今几乎成为了后端开发人员必须掌握的一项技能。即使你在生…

[Spring5]AOP底层原理

AOP底层原理 1.AOP底层使用动态代理 &#xff08;1&#xff09;有两种情况动态代理 第一种 有接口的情况&#xff0c;使用JDK动态代理 a.创建接口实现类代理对象&#xff0c;增强类的方法 第二种 没有接口的情况&#xff0c;使用CGLIB动态代理 a.创建子类的代理对象&#…

Hangfire定时触发作业,好像很简单?

【导读】本节我们继续稍微详细讲讲在我没有详细了解源码的前提下来探讨通过Hangfire定时触发作业有哪些需要注意的事项间隔时间内执行作业举个栗子&#xff0c;每隔10秒监控系统CPU&#xff0c;若CPU飙高&#xff08;根据实际业务定义百分比&#xff09;则在控制台打印输出&…

五分钟快速搭建Serverless免费邮件服务

1. 引言本文将带你快速基于 Azure Function 和 SendGrid 构建一个免费的Serverless&#xff08;无服务器&#xff09;的邮件发送服务&#xff0c;让你感受下Serverless的强大之处。该服务可以每月免费发送2,5000封&#xff0c;这是完全白嫖啊&#xff0c;感兴趣的&#xff0c;赶…

[Swagger2]SpringBoot集成Swagger

SpringBoot集成Swagger 引入依赖 <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger2 --> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.9.2</…

IdentityServer4 4.x版本 配置Scope的正确姿势

点击上方蓝字"小黑在哪里"关注我吧前言IdentityServer4 是为ASP.NET Core系列量身打造的一款基于 OpenID Connect 和 OAuth 2.0 认证的框架IdentityServer4官方文档&#xff1a;https://identityserver4.readthedocs.io/看这篇文章前默认你对IdentityServer4 已经有一…

[Swagger2]配置Swagger

配置Swaggr 1、Swagger实例Bean是Docket&#xff0c;所以通过配置Docket实例来配置Swaggger。 package com.xxxx.swagger2.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import springfox.doc…

.Net Core微服务入门全纪录(完结)——Ocelot与Swagger

点击上方蓝字"小黑在哪里"关注我吧前言上一篇【.Net Core微服务入门全纪录&#xff08;八&#xff09;——Docker Compose与容器网络】完成了docker-compose.yml文件的编写&#xff0c;最后使用docker compose的一个up指令即可在docker中运行整个复杂的环境。本篇简单…

[Swagger2]Swaggr配置扫描接口配置Swagger开关

Swagger配置扫描接口 1、构建Docket时通过select()方法配置怎么扫描接口。 Bean public Docket docket() {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()// 通过.select()方法&#xff0c;去配置扫描接口,RequestHandlerSelectors配置如何扫描…