手机版

ASP.NET芯实现单个节目的事件发布/订阅详解

时间:2021-08-27 来源:互联网 编辑:宝哥软件园 浏览:

背景

事件发布/订阅是一种非常强大的模式,可以帮助实现业务组件之间的完全解耦。不同的业务组件只依赖事件,只关注哪些事件需要自己处理,而不是谁来处理自己的发布事件。事件跟踪也基于事件发布/订阅。在微服务架构中,有很多事件发布/订阅的应用场景。今天,我将与大家分享一个基于ASP.NET核心的单个节目的事件发布/订阅示例。分布式项目的事件发布/订阅比较复杂,难点在于事务处理。稍后我会再写一篇博文来演示。

案例描述

目前,我们有一个基于ASP.NET核心的电子商务系统。项目刚开始的时候,业务很简单,只有一个购物车模块和一个订单模块,所有的代码都放在一个项目里。

整个项目采用简单的三层架构。

这里,当用户提交购物车时,程序将在ShoppingCartManager类的SubmitShoppingCart方法中执行三个操作

修改当前购物车的状态,根据购物车中的商品创建新订单,并将电子邮件代码发送给用户,如下所示:

public void SubmitShoppingCart(字符串shoppingCartId){ var shoppingCart=_ unitOfWork。ShoppingCartRepository。GetShoppingCart(shoppingCartId);_unitOfWork。ShoppingCartRepository。submit shoppingcart(shoppingCartId);_unitOfWork。订单存储库。creator der(new create ordered to { Items=shopping cart。物品。选择(p=新的新订单项目到{项目标识=项目标识,名称=项目名称,价格=项目价格})。to list()});//在这里,为了简化代码,我使用命令行来表示logic console . write line(‘确认电子邮件已发送’。”);_unitOfWork。save();}根据SOLID设计原则中的单一责任原则,如果一个类承担了太多的责任,就相当于将这些责任耦合在了一起。在这里生成订单和发送电子邮件不应该是当前SubmitShoppingCart的责任,所以我们需要将它们从这个方法中移除,使用的方法是事件订阅/发布。

新架构图

以下是发布/订阅事件后的系统架构图。

在这里,我们将创建一个购物车提交事件ShoppingCartSubmittedEvent。当站点启动时,我们将在名为EventHandlerContainer的类中注册两个处理类CreateOrderHandler和ConfirmEmailSentHandler,这两个处理类订阅了ShoppingCartSubmittedEvent事件。在SubmitShoppingCart方法中,我们将做两件事:更改当前购物车的状态。发布购物卡提交事件。CreateOrderHandler事件处理程序调用OrderManager类中的创建订单方法。确认邮件发送处理程序事件处理程序负责发送邮件。好了,让我们一步一步地实现上面描述的代码。

添加事件基类

这里我们先定义一个事件基类,其中只临时添加一个属性,表示当前事件的触发时间。

公共类event base { public event base(){ Entry don=DateTime。现在;}受保护的日期时间发生时间{ get设置;}}定义购物车提交事件

接下来,我们需要创建购物车提交事件类ShoppingCartSubmittedEvent,它继承自EventBase,并提供了购物项目的集合

public类shoppingcartcubmittedevent : event base { public shoppingcartcubmittedevent(){ Items=new list shoppingcartcubmitteditem();} public listshoppingCartSubmittedItem Items { get;设置;} }公共类ShoppingCartSubmittedItem {公共字符串ItemId { get设置;}公共字符串名称{ get设置;}公共十进制价格{ get设置;}}定义事件处理程序接口

为了添加一个事件处理程序,我们首先需要定义一个通用接口类IEventHandler

公共接口我知道了,其中T :事件库{ void Run(T obj);任务运行异步(T obj);}这个泛型接口类的是泛型类型必须继承自事件库类。接口提供了2个方法奔跑和运行异步。它们定义了该接口的实现类必须实现同一个处理逻辑的同步和异步方法。

为购物车提交事件编写事件处理器

有了事件处理器接口,接下来我们就可以开始为购物车提交事件添加事件处理器了。这里我们为了实现前面定义的逻辑,我们需要创建2个处理器CreateOrderHandler和确认邮件发送处理程序

CreateOrderHandler.cs

公共类CreateOrderHandler : ieventhallershoppingcarbitevent { private iordemanager _ order manager=null;public CreateOrderHandler(iordemanager order manager){ _ order manager=order manager;}公共void Run(shoppingCartSubmitdevent obj){ _ order manager .创建新订单(新模型dto。创建订单到{ Items=obj .项目。选择(p=新型号dto。新订单至{项目编号=p .项目编号,名称=p .名称,价格=p .价格}).to list()});}公共任务RunAsync(shoppingCartSubmitdevent obj){ 0返回任务Run(()={ Run(obj);});} }代码解释:

在CreateOrderHandler的构造函数中,我们注入了IOrderManager接口对象,创建新订单负责最终创建订单的工作这里为了简化代码,我直接使用了任务。快跑,并在其中调用了同步方法实现确认邮件发送处理程序。铯

公共类confideninemailsenthandler : ieventhaller跳购物车提交事件{ public void Run(shoppingcart提交事件obj){ Console .写线('确认电子邮件已发送');}公共任务RunAsync(shoppingCartSubmitdevent obj){ 0返回任务。运行(()={控制台。写线('确认电子邮件已发送');});} }代码解释:

这个处理类非常简单,为了简化代码,我仅输出了一行文本来表示实际需要运行的代码。为管理器类添加创建订单方法

IOrderManager.cs

公共接口IOrderManager { string new order(create order to to to);}OrderManager.cs

公共类订单管理器: IOrderManager { private IOrderRepository _ order repository;公共订单管理器(IOrderRepository order repository){ _ order repository=order repository;}公共字符串创建新订单(createorderto dto){ var order id=_ order repository .创建者der(dto);控制台一个订单创建d : { JsonConvert .serializeObject(dto)} ');返回orderId} }创建EventHandlerContainer

下面我们来编写最核心的事件处理器容器。在这里我们的事件处理器容器完成了3个功能

订阅事件(订阅活动)取消订阅事件(取消订阅事件)发布事件(发布事件)公共类EventHandlerContainer {私有iseservice provider _ service provider=null;私有静态Dictionarystring,list type _ mappings=new dictionary string,list type();public EventHandlerContainer(IServiceProvider服务提供程序){ _ service provider=service provider;} public static void SubscribeT,THandler()其中T :事件库其中THandler : ieventhandler { var name=类型为(T).名称;if(!_映射ContainsKey(name)) { _mappings .添加(名称,新列表类型{ });} _映射[名称]。添加(类型为(ThAnDler));}公共静态无效取消订阅,THandler()其中T :事件库其中THandler : ieventhandler { var name=类型为(T).名称;_映射[名称]。移除(类型为(ThAnDler));if (_mappings[name]).计数==0){ _映射。移除(名称);} } public void PublishT(T o),其中T :事件库{ var name=类型(T)} .名称;if(_映射ContainsKey(name)) { foreach (var处理程序in _ mappings[name]){ var service=(IEventHandlerT)_ service provider .GetService(处理程序);服务。运行(o);} } }公共异步任务发布时间(T o),其中T :事件库{ var name=typeof(T).名称;if(_映射ContainsKey(name)) { foreach (var处理程序in _ mappings[name]){ var service=(IEventHandlerT)_ service provider .GetService(处理程序);等待服务RunAsync(o);} } } }代码解释:

这里我没有直接订阅事件处理器的实例,而且订阅了事件处理器的类型多个事件处理器可以订阅同一个事件EventHandlerContainer的构造函数中,我们注入了一个IServiceProvider,我们可以使用它来获得对应事件处理器的实例。在程序启动时,注册事件订阅

现在我们来Startup.cs的配置服务方法,这里我们需要进行服务注册,并完成事件订阅。

public void ConfigureServices(IServiceCollection services){ services .AddMvc().SetCompatibilityVersion(兼容性版本. version _ 2 _ 2);服务AddScopedIOrderManager,order manager();服务AddScopedIShoppingCartManager,shoppingCartManager();服务addscope edishoppingcarrepository,shopping carpository();服务AddScopedIOrderRepository,order repository();服务AddScopedIUnitOfWork,UnitOfWork();服务AddScopedCreateOrderHandler();服务addscopedconfirmentemailsenhandler();服务AddScopedEventHandlerContainer();EventHandlerContainer .SubscribeShoppingCartSubmittedEvent,CreateOrderHandler();EventHandlerContainer .SubscribeShoppingCartSubmittedEvent,ConfirmEmailSentHandler();}注意:这里保证一个美国石油学会(美国石油协会)请求中的所有数据库操作在一个事务里,这里我们使用审视作用域。这样我们就可以在调用工作单元我工作接口的救援代码中启用事务。

修改购物卡管理器

最后我们来修改购物卡管理器,改用发布事件的方式来完成后续创建订单和发送邮件的功能。

public void SubmitShoppingCart(字符串shopping cartid){ var shopping cart=_ unitOfWork .购物卡片库.GetShoppingCart(shoppingCartId);_unitOfWork .购物卡片库.提交shopping cart(shopping cartid);_容器。发布(新的购物卡提交事件(){项目=购物卡.物品。选择(p=new ShoppingCartSubmittedItem { ItemId=p . ItemId,Name=p.Name,Price=p.Price }).to list()});_unitOfWork .save();}这样购物卡管理器就只需要关注购物车状态的变更,而不需要关注发送确认邮件和创建订单了。

最终效果

现在让我们启动项目,

首先我们使用[POST]/API/购物卡来添加一个新的购物车,这个应用程序接口会返回当前购物车的身份

然后我们使用[PUT]/API/购物车/购物车_ 6368728971405596来模拟提交购物车,程序返回操作成功

最后,让我们检查控制台的输出日志

两个事件处理程序都被正确触发。

摘要

至此,我们的代码重构完成了。在最后的代码中,SubmitShoppingCart方法只负责修改购物车状态和发布购物车提交的事件。生成订单和发送邮件的功能代码已经转移到独立的处理类。

这种方式的优势不仅在于代码的解耦,还在于后续的扩展。想想看,如果将来在当前项目需求中加入这样的功能,在提交购物车时,除了发送确认邮件外,还会发送手机短信。此时完全不需要修改ShoppingCartManager类,只需要为ShoppingCartSubmittedEvent添加一个新的事件处理程序即可,这也符合SOLID的开闭原则。

项目源代码:https://github.com/lamondlu/EventHandlerInSingleApplication

好了,这就是本文的全部内容。希望本文的内容对你的学习或工作有一定的参考价值。有问题可以留言交流。谢谢你的支持。

版权声明:ASP.NET芯实现单个节目的事件发布/订阅详解是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。