歡迎光臨
每天分享高質量文章

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

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

作者:Lamond Lu

地址:https://www.cnblogs.com/lwqlun/p/10640280.html[1]

原始碼:https://github.com/lamondlu/EventHandlerInSingleApplication[2]

背景

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

專案原始碼地址:https://github.com/lamondlu/EventHandlerInSingleApplication[3]

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

如果沒有看過之前的文章,建議你先看一下之前的實現,本文中的所有修改都是針對上一篇的代碼。

中介者樣式

中介者樣式,定義了一個中介物件來封裝一系列物件之間的交互關係。中介者使各個物件之間不需要顯式地相互取用,從而使耦合性降低,而且可以獨立地改變它們之間的交互行為。

中介者樣式是一種物件行為型樣式,其主要優點如下。

降低了物件之間的耦合性,使得物件易於獨立地被覆用。將物件間的一對多關聯轉變為一對一的關聯,提高系統的靈活性,使得系統易於維護和擴展。

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

什麼是MediatR

MediatR是一個基於.NET的中介者樣式實現庫,它是一種行程內訊息傳遞的方案,官網地址https://github.com/jbogard/MediatR/。[4]

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();EventHandlerContainer    .Subscribe();

這裡MediatR中已經提供了一個自動映射功能,它會在程式啟動時,自動搜索所有事件和事件處理器,並自動設置好它們之間的映射,所以我們就不需要在手動做這個事情了。

創建Notification

在我們之前的代碼中,我們定義了一個購物車提交事件,它繼承自事件基類EventBase

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

現在改用MediatR之後,我們需要修改當前事件的定義,讓它實現INotification接口。

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

NotificationHandler

完成事件定義部分的修改之後,我們還需要重構事件處理器的代碼。

在之前的代碼中,針對購物車提交事件,我們定義了兩個處理器,一個是創建訂單處理器CreateOrderHandler,一個是發送郵件處理器ConfirmEmailSentHandler

現在我們來使用INotificationHandler接口來改造之前定義好的兩個處理器。

    public class CreateOrderHandler : INotificationHandler    {        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    {        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的中介者樣式實現,它雖然只支持行程內的訊息傳遞,但是卻可以簡化事件發佈/訂閱代碼,幫助實現業務邏輯代碼的解耦,你可以自己試一試。

References

[1]https://www.cnblogs.com/lwqlun/p/10640280.html
[2]https://github.com/lamondlu/EventHandlerInSingleApplication
[3]https://github.com/lamondlu/EventHandlerInSingleApplication
[4]https://github.com/jbogard/MediatR/。

 

    赞(0)

    分享創造快樂