zoukankan      html  css  js  c++  java
  • 使用MediatR重构单体应用中的事件发布/订阅

    标题:使用MediatR重构单体应用中的事件发布/订阅
    作者:Lamond Lu
    地址:https://www.cnblogs.com/lwqlun/p/10640280.html
    源代码:https://github.com/lamondlu/EventHandlerInSingleApplication

    背景

    在之前的一篇文章中,我分享了一个在ASP.NET Core单体程序中,使用事件发布/订阅解耦业务逻辑的例子

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

    在文章评论中老张提到了使用MediatR的方案。对于MediatR,我以前只是听说的,没有认真研究过。上周末的胶东开发者技术沙龙中,衣哥也提到了这个库,闲暇时间我就研究了一下,并修改了之前的例子,发现确实简化了不少代码。

    如果没有看过之前的文章,建议你先看一下之前的实现,本文中的所有修改都是针对上一篇的代码。

    中介者模式

    中介者模式,定义了一个中介对象来封装一系列对象之间的交互关系。中介者使各个对象之间不需要显式地相互引用,从而使耦合性降低,而且可以独立地改变它们之间的交互行为。

    中介者模式是一种对象行为型模式,其主要优点如下。

    1. 降低了对象之间的耦合性,使得对象易于独立地被复用。
    2. 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。

    其实事件发布/订阅就是中介者模式的一种实现方式。

    什么是MediatR

    MediatR是一个基于.NET的中介者模式实现库,它是一种进程内消息传递的方案,官网地址https://github.com/jbogard/MediatR/。

    MediatR可以发送两种消息

    • 请求/响应消息,这种消息只有一个处理程序, 这种方式的消息需要实现IRequest接口, 其处理程序需要实现IRequestHandler接口
    • 通知消息,这种消息可以有一个或多个处理程序,这种方式的消息需要实现INotification接口, 其处理程序需要实现INotificationHandler接口

    从消息的特性上看,如果要改造我们之前的事件发布/订阅功能,我们需要使用通知消息,因为每个事件可能会有一个或多个的处理程序。

    添加MediatR

    在.NET Core中可以直接使用Nuget添加MediatR.Extensions.Microsoft.DependencyInjection库来引入MediatR

    Install-Package MediatR.Extensions.Microsoft.DependencyInjection

    添加完成后,我们还需要在Startup.cs中启动MediatR中间件。

    	public void ConfigureServices(IServiceCollection services)
        {
        	services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
            
            ...
    		services.AddMediatR();
    	}
    

    现在我们就可以在项目中使用MediatR了。

    提示:

    这里你可以会有疑问,之前的代码中,我们这里还定义了事件和处理器之间的映射,现在怎么就不需要了?

    EventHandlerContainer
    	.Subscribe<ShoppingCartSubmittedEvent, CreateOrderHandler>();
    EventHandlerContainer
    	.Subscribe<ShoppingCartSubmittedEvent, ConfirmEmailSentHandler>();
    

    这里MediatR中已经提供了一个自动映射功能,它会在程序启动时,自动搜索所有事件和事件处理器,并自动设置好它们之间的映射,所以我们就不需要在手动做这个事情了。

    创建Notification

    在我们之前的代码中,我们定义了一个购物车提交事件,它继承自事件基类EventBase

    	public class ShoppingCartSubmittedEvent : EventBase
        {
            public ShoppingCartSubmittedEvent()
            {
                Items = new List<ShoppingCartSubmittedItem>();
            }
    
            public List<ShoppingCartSubmittedItem> Items { get; set; }
        }
    

    现在改用MediatR之后,我们需要修改当前事件的定义,让它实现INotification接口。

    	public class ShoppingCartSubmittedEvent : INotification
        {
            public ShoppingCartSubmittedEvent()
            {
                Items = new List<ShoppingCartSubmittedItem>();
            }
    
            public List<ShoppingCartSubmittedItem> Items { get; set; }
        }
    

    NotificationHandler

    完成事件定义部分的修改之后,我们还需要重构事件处理器的代码。

    在之前的代码中,针对购物车提交事件,我们定义了两个处理器,一个是创建订单处理器CreateOrderHandler,一个是发送邮件处理器ConfirmEmailSentHandler

    现在我们来使用INotificationHandler接口来改造之前定义好的两个处理器。

    	public class CreateOrderHandler : INotificationHandler<ShoppingCartSubmittedEvent>
        {
            private IOrderManager _orderManager = null;
    
            public CreateOrderHandler(IOrderManager orderManager)
            {
                _orderManager = orderManager;
            }
    
            public Task Handle(ShoppingCartSubmittedEvent notification, CancellationToken cancellationToken)
            {
                _orderManager.CreateNewOrder(new Models.DTOs.CreateOrderDTO
                {
                    Items = notification.Items.Select(p => new Models.DTOs.NewOrderItemDTO
                    {
                        ItemId = p.ItemId,
                        Name = p.Name,
                        Price = p.Price
                    }).ToList()
                });
    
                return Task.CompletedTask;
            }
        }
    
    	public class ConfirmEmailSentHandler : INotificationHandler<ShoppingCartSubmittedEvent>
        {
            public Task Handle(ShoppingCartSubmittedEvent notification, CancellationToken cancellationToken)
            {
                Console.WriteLine("Confirm Email Sent.");
                return Task.CompletedTask;
            }
        }
    

    代码解释:

    • INotificationHandler是一个泛型接口,接口中定义的泛型类需要实现INotification接口
    • 当处理器实现INotificationHandler接口时,就需要实现一个Handle方法, 在该方法中,我们可以编写具体的业务代码
    • 从方法的返回值Task, 你可以了解到这个方法是没有返回值的,并且可以使用async/await变为一个异步的版本。

    发布事件

    在之前的代码中,当购物车提交成功之后,我们会在OrderManager类中,使用EventContainer发布事件。当我们使用MediatR之后,这部分代码稍有改动, 我们需要使用IMediator接口对象的Publish方法来发布事件。

    	public void SubmitShoppingCart(string shoppingCartId)
        {
        	var shoppingCart = _unitOfWork.ShoppingCartRepository.GetShoppingCart(shoppingCartId);
    
    		_unitOfWork.ShoppingCartRepository.SubmitShoppingCart(shoppingCartId);
    
    		_mediator.Publish(new ShoppingCartSubmittedEvent()
    		{
    			Items = shoppingCart.Items.Select(p => new ShoppingCartSubmittedItem
    			{
                	ItemId = p.ItemId,
    				Name = p.Name,
    				Price = p.Price
    			}).ToList()
    		});
    
    		_unitOfWork.Save();
    	}
    

    最终效果

    至此,所有代码就都完成了,我们可以按照上一篇的操作步骤,再测试一次。

    当执行购物车提交操作的时候,订单创建和邮件发送处理器都正确触发了。

    总结

    MediatR是一个基于.NET的中介者模式实现,它虽然只支持进程内的消息传递,但是却可以简化事件发布/订阅代码,帮助实现业务逻辑代码的解耦,你可以自己试一试。

  • 相关阅读:
    MKMapVIew学习系列2 在地图上绘制出你运行的轨迹
    WPF SDK研究 Intro(6) WordGame1
    WPF SDK研究 Intro(3) QuickStart3
    WPF SDK研究 Layout(1) Grid
    WPF SDK研究 目录 前言
    WPF SDK研究 Intro(7) WordGame2
    WPF SDK研究 Layout(2) GridComplex
    对vs2005创建的WPF模板分析
    WPF SDK研究 Intro(4) QuickStart4
    《Programming WPF》翻译 第6章 资源
  • 原文地址:https://www.cnblogs.com/lwqlun/p/10640280.html
Copyright © 2011-2022 走看看