.NET Core开发实战(第35课:MediatR:让领域事件处理更加优雅)--学习笔记

35 | MediatR:让领域事件处理更加优雅

核心对象

IMediator

INotification

INotificationHandler

这两个与之前的 Request 的行为是不一样的,接下来看一下代码

internal class MyEvent : INotification
{public string EventName { get; set; }
}internal class MyEventHandler : INotificationHandler<MyEvent>
{public Task Handle(MyEvent notification, CancellationToken cancellationToken){Console.WriteLine($"MyEventHandler执行:{notification.EventName}");return Task.CompletedTask;}
}internal class MyEventHandlerV2 : INotificationHandler<MyEvent>
{public Task Handle(MyEvent notification, CancellationToken cancellationToken){Console.WriteLine($"MyEventHandlerV2执行:{notification.EventName}");return Task.CompletedTask;}
}
//await mediator.Send(new MyCommand { CommandName = "cmd01" });
await mediator.Publish(new MyEvent { EventName = "event01" });

之前 mediator 使用了 Send 的方式来处理 Command,它还有一个方法 Publish,这个方法的入参是一个 INotification

启动程序,输出如下:

MyEventHandler执行:event01
MyEventHandlerV2执行:event01

与之前的 IRequest 不同的是,INotification 是可以注册多个 Handler 的,它是一个一对多的关系,借助它就可以对领域事件定义多个处理器来处理

接着看一下之前云服务的代码

public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default)
{var result = await base.SaveChangesAsync(cancellationToken);await _mediator.DispatchDomainEventsAsync(this);return true;
}

之前在 IUnitOfWork 定义的时候讲过一个发送领域事件的方法 DispatchDomainEventsAsync,看一下这个方法的定义

static class MediatorExtension
{public static async Task DispatchDomainEventsAsync(this IMediator mediator, DbContext ctx){var domainEntities = ctx.ChangeTracker.Entries<Entity>().Where(x => x.Entity.DomainEvents != null && x.Entity.DomainEvents.Any());var domainEvents = domainEntities.SelectMany(x => x.Entity.DomainEvents).ToList();domainEntities.ToList().ForEach(entity => entity.Entity.ClearDomainEvents());foreach (var domainEvent in domainEvents)await mediator.Publish(domainEvent);}
}

可以看到这里是将所有的实体内的领域事件全部都查找出来,然后通过 mediator 的 Publish 发送领域事件,具体的领域事件的处理注册在 mediator 里面,这里定义了一个 OrderCreatedDomainEventHandler

public class OrderCreatedDomainEventHandler : IDomainEventHandler<OrderCreatedDomainEvent>
{ICapPublisher _capPublisher;public OrderCreatedDomainEventHandler(ICapPublisher capPublisher){_capPublisher = capPublisher;}public async Task Handle(OrderCreatedDomainEvent notification, CancellationToken cancellationToken){await _capPublisher.PublishAsync("OrderCreated", new OrderCreatedIntegrationEvent(notification.Order.Id));}
}

它继承自 IDomainEventHandler,而 IDomainEventHandler 继承自 INotificationHandler

public interface IDomainEventHandler<TDomainEvent> : INotificationHandler<TDomainEvent>where TDomainEvent : IDomainEvent
{//这里我们使用了INotificationHandler的Handle方法来作为处理方法的定义Task Handle(TDomainEvent domainEvent, CancellationToken cancellationToken);
}

这也就是为什么 IDomainEventHandler 会识别到 DomainEvent 并且进行处理,同样的在定义 DomainEvent 的时候,也需要标识它是一个 DomainEvent

public class OrderCreatedDomainEvent : IDomainEvent
{public Order Order { get; private set; }public OrderCreatedDomainEvent(Order order){this.Order = order;}
}

而 DomainEvent 实际上也是继承自 INotification

public interface IDomainEvent : INotification
{
}

这也就意味着 EventHandler 可以正确的识别到对应的 Event 并且进行处理,这都是 MediatR 的核心能力

领域事件都是定义在 event 目录下,与领域模型定义在一起,所有的领域事件都继承 DomainEvent,分布于这个目录

领域事件的处理 Handler 都定义在 Application 应用层的 Application 下面的 DomainEventHandlers 目录下面

这样的好处是事件的定义与事件的处理是分开的,并且非常的明确知道有哪些领域事件,有哪些领域事件的处理程序

关于 MediatR 再补充一部分内容,在 TransactionBehavior 内可以看到这个类实际上继承自 IPipelineBehavior

namespace MediatR
{public interface IPipelineBehavior<in TRequest, TResponse>{Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next);}
}

这个接口的作用是在命令或者事件处理的之前或者之后插入逻辑,它的执行的方式有点像中间件的方式,在 Handler 的入参里面有一个 next 的参数,就是指 CommandHandler 或者 EventHandler 的执行的逻辑,在这里就可以决定 Handler 的具体执行之前或者之后,插入一些逻辑

public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{var response = default(TResponse);var typeName = request.GetGenericTypeName();try{// 首先判断当前是否有开启事务if (_dbContext.HasActiveTransaction){return await next();}// 定义了一个数据库操作执行的策略,比如说可以在里面嵌入一些重试的逻辑,这里创建了一个默认的策略var strategy = _dbContext.Database.CreateExecutionStrategy();await strategy.ExecuteAsync(async () =>{Guid transactionId;using (var transaction = await _dbContext.BeginTransactionAsync())using (_logger.BeginScope("TransactionContext:{TransactionId}", transaction.TransactionId)){_logger.LogInformation("----- 开始事务 {TransactionId} ({@Command})", transaction.TransactionId, typeName, request);response = await next();// next 实际上是指我们的后续操作,这里的模式有点像之前讲的中间件模式_logger.LogInformation("----- 提交事务 {TransactionId} {CommandName}", transaction.TransactionId, typeName);await _dbContext.CommitTransactionAsync(transaction);transactionId = transaction.TransactionId;}});return response;}catch (Exception ex){_logger.LogError(ex, "处理事务出错 {CommandName} ({@Command})", typeName, request);throw;}
}

这里实现里在执行命令之前判断事务是否开启,如果事务开启的话继续执行后面的逻辑,如果事务没有开启,先开启事务,再执行后面的逻辑

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

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

相关文章

LeetCode 559N叉树的最大深度-简单

给定一个 N 叉树&#xff0c;找到其最大深度。 最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。 N 叉树输入按层序遍历序列化表示&#xff0c;每组子节点由空值分隔&#xff08;请参见示例&#xff09;。 示例 1&#xff1a; 输入&#xff1a;root [1,null,3,…

android 5.0状态栏下载地址,Android沉浸式状态栏(5.0以上系统)

Android沉浸式状态栏(5.0以上系统)沉浸式状态栏可以分为两种:1.直接给状态栏设置颜色 (如下图:)这里写图片描述java代码形式:if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {Window window activity.getWindow();window.addFlags(WindowManager.LayoutParams…

解析“60k”大佬的19道C#面试题(下)

解析“60k”大佬的19道C#面试题&#xff08;下&#xff09;在上篇中&#xff0c;我解析了前 10 道题目&#xff0c;本篇我将尝试解析后面剩下的所有题目。解析“60k”大佬的19道C#面试题&#xff08;上&#xff09;这些题目确实不怎么经常使用&#xff0c;因此在后文中&#xf…

LeetCode 543二叉树的直径-简单

给定一棵二叉树&#xff0c;你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。 示例 : 给定二叉树 1/ \2 3/ \ 4 5 返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。 代码如下: /*** Def…

android 卡顿代码定位,Android 性能优化实例:通过 TraceView 定位卡顿问题

8种机械键盘轴体对比本人程序员&#xff0c;要买一个写代码的键盘&#xff0c;请问红轴和茶轴怎么选&#xff1f;背景项目中使用了鸿洋大神的TreeView树状结构控件&#xff0c; 但是由于在主线程中使用了注解/反射来定位节点&#xff0c; 内容一多就有点卡顿。因此通过android …

DotNetCore三大Redis客户端对比和使用心得

前言稍微复杂一点的互联网项目&#xff0c;技术选型都会涉及Redis&#xff0c;.NetCore的生态越发完善&#xff0c;支持.NetCore的Redis客户端越来越多&#xff0c;下面三款常见的Redis客户端&#xff0c;相信大家平时或多或少用到一些&#xff0c;结合三款客户端的使用经历&am…

LeetCode 226翻转二叉树-简单

翻转一棵二叉树。 示例&#xff1a; 输入&#xff1a; 4/ \2 7/ \ / \ 1 3 6 9输出&#xff1a; 4/ \7 2/ \ / \ 9 6 3 1代码如下: /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode…

android elevation 白色,Android Elevation

简介&#xff1a;在Android API21&#xff0c;新添加了一个属性&#xff1a;android:elevation&#xff0c;用以在xml定义View的深度(高度)&#xff0c;也即z方向的值。除了elevation之外&#xff0c;类似于已有的translationX、translationY&#xff0c;也相对应地新增了一个t…

(译)创建.NET Core多租户应用程序-租户解析

介绍本系列博客文章探讨了如何在ASP.NET Core Web应用程序中实现多租户。这里有很多代码段&#xff0c;因此您可以按照自己的示例应用程序进行操作。在此过程的最后&#xff0c;没有对应的NuGet程序包&#xff0c;但这是一个很好的学习和练习。它涉及到框架的一些“核心”部分。…

LeetCode 404左叶子之和-简单

计算给定二叉树的所有左叶子之和。 示例&#xff1a; 3/ \9 20/ \15 7在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&#xff0c;所以返回 24 代码如下: /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode…

android apt最新版本,解决Android studio 2.3升级到Android studio 3.0 后apt报错问题

解决Android studio 2.3升级到Android studio 3.0 后apt报错问题发布时间&#xff1a;2020-09-16 19:59:42来源&#xff1a;脚本之家阅读&#xff1a;62作者&#xff1a;哈特中尉1.现象描述原来项目在Android studio 2.3一切正常&#xff0c;升级3.0之后报如下错误&#xff1a;…

【要闻】Kubernetes无用论诞生、Elasticsearch 7.6.2 发布

导读&#xff1a;本期要闻包含OpenStack网络如何给组织带来好处、Portworx CEO分享的如何让Kubernetes跑得快还不出错的秘籍等精彩内容。大数据要闻Elasticsearch 7.6.2 发布&#xff0c;分布式搜索和数据分析引擎Elasticsearch 7.6.2 发布了&#xff0c;Elasticsearch 是一个分…

android中弹出窗口,如何在Android中创建弹出窗口(PopupWindow)

如何制作一个简单的Android弹出窗口这是一个更完整的例子。这是一个补充性答案&#xff0c;涉及一般情况下创建弹出窗口的过程&#xff0c;而不一定是OP问题的具体细节。(OP要求取消按钮&#xff0c;但这不是必需的&#xff0c;因为用户可以在屏幕上的任意位置单击以将其取消。…

LeetCode 04检查平衡性-简单

实现一个函数&#xff0c;检查二叉树是否平衡。在这个问题中&#xff0c;平衡树的定义如下&#xff1a;任意一个节点&#xff0c;其两棵子树的高度差不超过 1。 示例 1: 给定二叉树 [3,9,20,null,null,15,7] 3/ \9 20/ \15 7返回 true 。 示例 2: 给定二叉树 [1,2,2,3,…

玩转控件:对Dev中GridControl控件的封装和扩展

清明节清明时节雨纷纷路上行人欲断魂借问酒家何处有牧童遥指杏花村又是一年清明节至&#xff0c;细雨绵绵犹如泪光&#xff0c;树叶随风摆动....转眼间&#xff0c;一年又过去了三分之一&#xff0c;疫情的严峻让不少企业就跟清明时节的树叶一样&#xff0c;摇摇欲坠。裁员的裁…

C++ using namespace 命名空间的定义与使用

#include <iostream> using namespace std;namespace A {int x, y;void fun() {cout << "hello world" << endl;} }int main() {using namespace A;cout << x << endl;fun();return 0; }

android webview framework,android – Webview导致ANR

我编写了一个应用程序,它在Webview中显示html页面,这些页面在ViewPager中管理.一切正常,但是从一页翻到另一页时我有几个ANR. ANR数据转储显示主线程已通过ThreadedRenderer.nSyncAndDrawFrame()调用它调用本机方法,该方法显然与Renderer线程通信,并等待该线程完成绘制视图.由于…

创业5年,我有5点关于人的思考

点击蓝字关注&#xff0c;回复“职场进阶”获取职场进阶精品资料一份不知不觉创业五年了&#xff0c;也算一个屡战屡败、屡败屡战的创业老兵了。从第一次失败要靠吃抗抑郁的药&#xff0c;到现在理性的看待成败得失&#xff0c;不得不说&#xff0c;创业这条路对我还是有不小提…

C++实现具有[数组]相似特征的类DoubleSubscriptArray

#include <iostream> using namespace std;class DoubleSubscriptArray {public:DoubleSubscriptArray(int x, int y) {p new int *[x];//行 //申请行的空间for (int i 0; i < x; i) {p[i] new int [y];//每行的列申请空间}for (int i 0; i < x; i)for (int j …

Docker-HealthCheck指令探测ASP.NET Core容器健康状态

写在前面HealthCheck 不仅是对应用程序内运行情况、数据流通情况进行检查&#xff0c;还包括应用程序对外部服务或依赖资源的健康检查。健康检查通常是以暴露应用程序的HTTP端点的形式实施&#xff0c;可用于配置健康探测的的场景有 &#xff1a;容器或负载均衡器 探测应用状态…