標題:使用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/。