使用MediatR重構單體應用中的事件發佈/訂閱

標題:使用MediatR重構單體應用中的事件發佈/訂閱
做者:Lamond Lu
地址:http://www.javashuo.com/article/p-vffburvs-eh.html
源代碼:https://github.com/lamondlu/EventHandlerInSingleApplicationhtml

背景

在以前的一篇文章中,我分享了一個在ASP.NET Core單體程序中,使用事件發佈/訂閱解耦業務邏輯的例子git

項目源代碼地址:https://github.com/lamondlu/EventHandlerInSingleApplicationgithub

在文章評論中老張提到了使用MediatR的方案。對於MediatR,我之前只是據說的,沒有認真研究過。上週末的膠東開發者技術沙龍中,衣哥也提到了這個庫,閒暇時間我就研究了一下,並修改了以前的例子,發現確實簡化了很多代碼。c#

若是沒有看過以前的文章,建議你先看一下以前的實現,本文中的全部修改都是針對上一篇的代碼。異步

中介者模式

中介者模式,定義了一箇中介對象來封裝一系列對象之間的交互關係。中介者使各個對象之間不須要顯式地相互引用,從而使耦合性下降,並且能夠獨立地改變它們之間的交互行爲。async

中介者模式是一種對象行爲型模式,其主要優勢以下。測試

  1. 下降了對象之間的耦合性,使得對象易於獨立地被複用。
  2. 將對象間的一對多關聯轉變爲一對一的關聯,提升系統的靈活性,使得系統易於維護和擴展。

其實事件發佈/訂閱就是中介者模式的一種實現方式。code

什麼是MediatR

MediatR是一個基於.NET的中介者模式實現庫,它是一種進程內消息傳遞的方案,官網地址https://github.com/jbogard/MediatR/。htm

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的中介者模式實現,它雖然只支持進程內的消息傳遞,可是卻能夠簡化事件發佈/訂閱代碼,幫助實現業務邏輯代碼的解耦,你能夠本身試一試。

相關文章
相關標籤/搜索