標題:使用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/。
知識星球