MediatR 在.NET应用中的实践

MediatR 简介

bd75290cf7a16beb5516c7e4d35ec9cb.pngMediatR.NET中的开源简单中介者模式实现.它通过一种进程内消息传递机制(无其他外部依赖),进行请求/响应、命令、查询、通知和事件的消息传递,并通过泛型来支持消息的智能调度。开源库地址是https://github.com/jbogard/MediatR

MediatR的作者是Jimmy Bogard,他也是大名鼎鼎的AutoMapper的作者。如果你的英文还不错,推荐你到https://jimmybogard.com上拜读一下他博客文章,相信对你会有益处的。

中介者模式

既然MediatR是中介者模式的一种实现,那么我们有必要简单的了解一下什么是中介者模式。

定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。中介者模式又叫调停模式,它是迪米特法则的典型应用。

普通模式下,常常会出现好多对象之间存在复杂的直接交互关系,这种交互关系常常是“网状结构”,它要求每个对象都必须知道它需要交互的对象。在现实生活中,比如房产买卖如果没有中介的情况,卖方要与无数的买方联系,买方也与无数卖方联系,通过极为复杂的网状沟通才能获得自己心仪的买(卖)家。d06463e0365f25ea8391bae435784c22.png如果把这种“网状结构”改为“星形结构”的话,将大大降低它们之间的“耦合性”,这时只要找一个“中介者”就可以了。单一的买方或卖方都只跟一个中介服务人员联系,中介会帮你甄别联系另外一方。这样将大大降低对象之间的耦合性。38050c31b5596d1acc647ac793cb5bec.png中介者模式是一种对象行为型模式,其主要优点:

  • 类之间各司其职,符合迪米特法则;

  • 降低了对象之间的耦合性,使得对象易于独立地被复用;

  • 将对象间的一对多关联转变为一对一的关联,提高灵活性,便于于维护和扩展。

MediatR 服务的注册

添加Nuget包

在Visual Studio中添加下图两个包:00f2fde6eafb57c31ef934b00c7181b3.png也可以通过命令行工具添加:

dotnet add package MediatR
dotnet add package MediatR.Extensions.Microsoft.DependencyInjection

注册MediatR

.NET 6之前的ASP.NET Core项目中需要在Startup类中添加一下注册:a7c35b8ae690c963c81b615778e20dbf.png.NET 6的应用中,则可在Program中添加注册:f58282942811d8e72b6b4d775cdc3f67.png

MediatR 的基本用法

MediatR有两种消息传递的方式:

  • Request/Response,用于一个单独的Handler。

  • Notification,用于多个Handler。

Request/Response

Request/Response 有点类似于 HTTP 的 Request/Response,发出一个 Request 会得到一个 Response。

Request 消息在 MediatR 中,有两种类型:

  • IRequest<T> 返回一个T类型的值。

  • IRequest 不返回值。

对于每个 request 类型,都有相应的 handler 接口:

  • IRequestHandler<T, U> 实现该接口并返回 Task<U>

  • RequestHandler<T, U> 继承该类并返回 U

  • IRequestHandler<T> 实现该接口并返回 Task<Unit>

  • AsyncRequestHandler<T> 继承该类并返回 Task

  • RequestHandler<T> 继承该类不返回

这样一个创建订单的命令和对应的处理程序,就如下图所示:45aa9714a1d598cd697a4c8da4e21590.png而在 Controller 中使用时,就简单如下:428c4bcc7c17fe1befd1ee3a2847d642.png

Notification

Notification 就是通知,调用者发出一次,然后可以有多个处理者参与处理。4eb522189db06f801f0bbfa525e17b5d.pngNotification 消息的定义很简单,只需要让你的类继承一个空接口 INotification 即可。而处理程序则实现 INotificationHandler<T> 接口的 Handle 方法就行:11a17b533056ab554ba35099c534cc74.png有了上述定义后,只需要一行代码即可完成调用:

await _mediator.Publish(new QueryOrder());

然后,我们会得到如下结果:b039143424858aaf6cccdffcc59c44b3.png是不是很简单呢?

注意:

「默认情况下」 通知的执行过程不是异步的。Publish 方法调用后,MediatR 会将所有该通知的Handler依次执行完好返回。也就是说如果一个通知的handler执行需要1秒钟,共有3个handler,则这个通知的Publish方法会执行3秒钟。作者在 Github 的 MediatR 库中,给出了各种丰富场景的通知处理调度程序样例代码,开发者可以根据自己的业务情况自行定制修改 MediatR 的默认通知调度模式。

ISender 与 IPublisher

前面的例子中,我们都是直接使用的IMediator接口服务进行调用,MediatR 的作者在发布 9.0.0 版时,有意把原本孤立大一统的 IMediator 接口拆成了两个 ISender 和 IPublisher,分别仅用于 Reuest/Response 和 Notification 场景,即:

  • ISender 接口只有 Send 方法

  • IPublisher 接口只有 Publish 方法

MediatR 的管线

.NET Core 一个大量存在但是被不少人忽视的概念就是 Pipeline,也就是管线。比如,ASP.NET Core 中的管线模型大概如下图:6d1a8e6422ecc261c3e1fa8d698e7745.png这套管线模型可以使得我们在 HTTP Request 的真正处理逻辑之前,经过一层层的管线逻辑对数据做预处理或者鉴权等;也可在处理逻辑返回结果后,在调用者得到响应前,由管线对结果进行二次加工。这就给我们带来一个很好的分工协作模型,可以轻松应对「必然变化的客户需求」,而不必修改核心业务逻辑代码。毕竟,你知道「客户的需求经常还要改回去」

MediatR 中具有与此类似的管线机制,可通过泛型接口 IPipelineBehavior<,> 来定义:df503b2fb7df556390125fc601cd0448.png使得我们在 Handler 的 Handle 真正执行前或后可以额外做一些事情:记录日志、对消息做校验、对数据做预处理(如:把中文逗号改为英文逗号)、记录性能较差的Handler 等等。

下面是我们对一个处理时长超过2秒的进行预警日志记录的情景:ea91acfedfb0b78eec97bae55b914657.png

这时候可能有人会问了,我们怎么控制管线的执行顺序呢?嗯,这个问题很好,作者也早就想到了,MeidatR 的管线是通过注册的顺序来决定执行的顺序的。0a746c009a4df2b9ade360efc1c7830a.png上图中的性能记录管线(RequestPerformanceBehavior)就会比数据验证管线(RequestValidationBehavior)先执行,毕竟验证数据有时候也是需要花一些时间的。

「消息验证管线是一个相对复杂的场景,我会在之后另起一篇单独进行分享和说明。」

注意

MediatR 中的管线有两个比较特殊的预定:

  • IRequestPreProcessor<> 请求执行前的预处理

  • IRequestPostProcessor<,> 请求执行后的再处理

他们两个的实现不必单独注册,在默认 MediatR 注册逻辑中会自动注册好,他们在所有管线中执行的位置顺序也就显而易见了。

CQRS or DDD?

软件开发发展到今天,模式和理念不断在架构中刷新:从分布式到微服务,再到云原生 ……。时代对一个程序员,尤其是服务端程序员,提出的要求越来越高。DDD(领域驱动设计)在微服务架构中一再被提及,甚至有人提出这是必须项!

实施一个完美的 DDD 还是有难度的,现实中还有很多奋战在一线的 CRUD 程序员还是不少。那么在 CRUD 和 DDD 之间我们是否还有缓冲区呢?MediatR 的作者曾经也撰文讨论过这个问题,我很认同他的基本观点:设计是为应用服务的,不能为了 DDD 而 DDD。

CQRS 的全称是:"Command and Query Responsibility Segregation",直译过来就是命令与查询责任分离,可以通俗的理解为 读写分离

9b0a5a89d03b759e9ddcc20ded0de65d.png

微软的官方文档中对此做过如下陈述:

CQRS 命令和查询责任分离数据存储的读取和更新操作分离的模式。在应用程序中实现 CQRS 可以最大程度地提高其性能、可伸缩性和安全性。通过迁移到 CQRS 而创建的灵活性使系统能够随着时间的推移更好地发展,并防止更新命令在域级别导致合并冲突。

微软也给出了相应的隔离模型解决方案:

CQRS 使用命令来更新数据,使用查询来读取数据,将读取和写入 分离到不同的 模型中。

  • 命令应基于任务,而不是以数据为中心。

  • 命令可以放置在队列中进行异步处理,而不是同步处理。

  • 查询从不修改数据库。查询返回的 DTO 不封装任何域知识。

005af8fd18852a0b462bd6b2279dba1d.png

CQRS 的好处包括:

  • 「独立缩放」: CQRS 允许读取和写入工作负载独立缩放,这可能会减少锁争用。

  • 「优化的数据架构」: 读取端可使用针对查询优化的架构,写入端可使用针对更新优化的架构。

  • 「安全性」: 更轻松地确保仅正确的域实体对数据执行写入操作。

  • 「关注点分离」: 分离读取和写入端可使模型更易维护且更灵活。大多数复杂的业务逻辑被分到写模型。读模型会变得相对简单。

  • 「查询更简单」: 通过将具体化视图存储在读取数据库中,应用程序可在查询时避免复杂联接。

有了 MediatR 我们可以在应用中轻松实现 CQRS:

  • IRequest<> 的消息名称以 Command 为结尾的是命令,其对应的 Handler 执行「写」任务

  • IRequest<> 的消息名称以 Query 为结尾的是查询,其对应的 Handler 执行「读」数据

结束语

MediatR 是一个简单的中介者实现,可以极大降低我们的应用复杂度,也能够使得我们一路从 CRUD 到 CQRS 到 DDD 进行逐级演进。毕竟我们是生活在现实中的人,不能罔顾商业现实,纯粹一味追求技术。65f731c020fc28b6a1cb3ecd28676855.png

商业技术的演进,应该是一路持续的改革而不是来一场革命。疫情总有反复,但是我们得活着,相对轻松的活着!

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

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

相关文章

java 录屏_java 录屏 小工具源码(idea)

【实例简介】录制的视频保存在 java.io.tmpdir 目录&#xff0c;windows通常为 C:\Users\Administrator\AppData\Local\Temp【实例截图】点击播放后&#xff0c;效果如下&#xff1a;【核心代码】import java.awt.AWTException;import java.awt.Color;import java.awt.Dimensio…

Java - 强引用、弱引用、软引用、虚引用

1、强引用&#xff08;StrongReference&#xff09; 强引用是使用最普遍的引用。如果一个对象具有强引用&#xff0c;那垃圾回收器绝不会回收它。如下&#xff1a; Object onew Object(); // 强引用 当内存空间不足&#xff0c;Java虚拟机宁愿抛出OutOfMemoryError错误&am…

dede织梦5.7,后台采集数据导入,空文章过滤.

为什么80%的码农都做不了架构师&#xff1f;>>> 后台目录下 dede/co_export.php 186行左右 else if($itemName litpic){$litpic trim($ctag->GetInnerText());} } 下面添加&#xff0c;变成 else if($itemName litpic){$litpic trim($ctag->GetInnerT…

真正的男人要勇于承担责任......

1 下个月可以住贵一点的房子了&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼2 当灯牌坏了一个&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼3 当前男友提着钱上门▼4 真正的男人要勇于承担责任&#xff08;via.豆瓣 pink&#xff09;▼5 &#xff1f…

WPF里面的常用笔刷

程序运行效果 <Window x:Class"This_brush.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"Title"MainWindow" Height"350" Width…

没想到,错误的单例写法,让 RabbitMQ 大量超时导致程序挂死!

一&#xff1a;背景 1. 讲故事10月份星球里的一位老朋友找到我&#xff0c;说他们公司的程序在一个网红直播带货下给弄得无响应了&#xff0c;无响应期间有大量的 RabbitMQ 超时&#xff0c;寻求如何找到根源&#xff0c;聊天截图我就不发了。既然无响应了&#xff0c;那必然是…

Android之OKHttp使用总结

介绍: OkHttp是一个高效的HTTP库: 持 SPDY ,共享同一个Socket来处理同一个服务器的所有请求如果SPDY不可用,则通过连接池来减少请求延时无缝的支持GZIP来减少数据流量缓存响应数据来减少重复的网络请求 会从很多常用的连接问题中自动恢复。如果您的服务器配置了多个IP地址…

开车走吗?朋友......

1 冬天出门有多难&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼2 这张图你们明白了什么&#xff1f;&#xff08;素材来源网络&#xff0c;侵删&#xff09;▼3 奥特曼不用吃饭&#xff08;via.杰克波比&#xff0c;侵删&#xff09;▼4 为什么没人敢动孟婆&…

WPF中使用资源

这节讲一下如何在WPF中使用资源。01了解资源在编程中&#xff0c;一个变量&#xff0c;一段代码&#xff0c;一张图片&#xff0c;一段视频或者音频&#xff0c;这种可以拿来为我所用的东西就可以称之为资源&#xff0c;一个让人眼前一亮的程序&#xff0c;可能会使用到许许多多…

清华放大招!竟然连初三学生都招,一条龙培养到博士,还不准转专业......

全世界只有3.14 % 的人关注了爆炸吧知识提前5年博士毕业近日&#xff0c;清华大学本科招生网发布了一则重要公告——《清华大学2021年丘成桐数学科学领军人才培养计划招生办法》。在这则公告中&#xff0c;初三生&#xff0c;你没看错&#xff0c;是没有参加过中考、高考的初三…

一键生成Vue.js + Web API前后端集成项目

前言默认情况下&#xff0c;Visual Studio提供了“基于Vue.js Web 应用程序”项目模板&#xff0c;可以生成Vue.js前端项目。你需要另外创建Web API项目&#xff0c;调试时需要同时启动2个项目&#xff0c;然后还要解决前后端集成带来的问题&#xff0c;比如跨域访问。如果&…

【DB2学习文档之七】SQL for DB2

作者&#xff1a;gnuhpc 出处&#xff1a;http://www.cnblogs.com/gnuhpc/ 1.SQL的数据操作语言data manipulation language (DML) 参见Beginning SQL Queries: From Novice to Professional, by Clare Churcher (Apress, 2008) 2.Select语句 这个语句是DB2中最简单也最复杂的语…

这些让人看瞎了的设计!实力证明,谁才是世界的最终boss!

全世界只有3.14 % 的人关注了爆炸吧知识我瞎了也懵了昨天知识君刷微博&#xff0c;看到了一组动图&#xff0c;一时间我都不知道是我的眼睛出了错&#xff0c;还是我的大脑反应不过来。奇了怪了&#xff01;怎么箭头反转了180后&#xff0c;还是原样&#xff01;问题到底出在哪…

弹出框css技巧

2019独角兽企业重金招聘Python工程师标准>>> 技术要点&#xff1a; 一个覆盖整个屏幕的浅灰色背景的div,一个包含内容的的div, 代码如下&#xff1a; <div id"financeTip" style""><div class"financeTipBg" style"&qu…

在业务层实现校验请求参数

前言在前面的文章中&#xff0c;我们介绍了在业务层实现管道模式&#xff1a;响应缓存记录请求日志今天&#xff0c;我们同样使用IPipelineBehavior&#xff0c;介绍如何在业务层实现校验请求参数&#xff0c;用于检查输入是否满足业务要求。Demo首先&#xff0c;创建ASP.NET C…

静电可以有多好玩?

1 静电可以有多好玩&#xff1f;孩子&#xff1a;知道我怎么秃的了吧2 这就是爱情啊3 今年最佳cos4 别人家的狗能当桌子▼你家的狗……&#xff08;主银&#xff0c;我就这样静静的看着你&#xff09;▼5 含羞草6 可以让我骑一下你吗7 总之&#xff0c;离我远点你点的每个赞&am…

+操作符重载(2)

2019独角兽企业重金招聘Python工程师标准>>> #include <iostream> class C_A { public: int Cn; }; struct S_A { public: int Sn; }; enum E_A { En 12 }; C_A operator(int n,C_A cVar) { cVar.Cn n; return cVar; …

WPF 四种不同效果呼吸灯

WPF开发者QQ群&#xff1a; 340500857 | 微信群 -> 进入公众号主页 加入组织由于微信群人数太多入群请添加小编微信号&#xff08;yanjinhuawechat&#xff09;或&#xff08;W_Feng_aiQ&#xff09;邀请入群&#xff08;需备注WPF开发者&#xff09;PS&#xff1a;有更好的…

用GCD线程组与GCD信号量将异步线程转换为同步线程

用GCD线程组与GCD信号量将异步线程转换为同步线程 有时候我们会碰到这样子的一种情形: 同时获取两个网络请求的数据,但是网络请求是异步的,我们需要获取到两个网络请求的数据之后才能够进行下一步的操作,这个时候,就是线程组与信号量的用武之地了. 线程组用以监听线程的执行情况…

基于visual Studio2013解决C语言竞赛题之0710排序函数

&#xfeff;&#xfeff;题目解决代码及点评/* 10、用指向指针的指针的方法对N个整数排序并输出。 要求排序单独写成一个函数。N个整数和N在主程序中输入&#xff0c;最后在主函数中输出。 */ #include <stdio.h> #include <stdlib.h> #define N 10 void main() {…