标题:使用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
中介者模式是一种对象行为型模式,其主要优势以下。测试
其实事件发布/订阅就是中介者模式的一种实现方式。code
MediatR是一个基于.NET的中介者模式实现库,它是一种进程内消息传递的方案,官网地址https://github.com/jbogard/MediatR/。htm
MediatR能够发送两种消息中间件
IRequest
接口, 其处理程序须要实现IRequestHandler
接口INotification
接口, 其处理程序须要实现INotificationHandler
接口从消息的特性上看,若是要改造咱们以前的事件发布/订阅功能,咱们须要使用通知消息,由于每一个事件可能会有一个或多个的处理程序。
在.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中已经提供了一个自动映射功能,它会在程序启动时,自动搜索全部事件和事件处理器,并自动设置好它们之间的映射,因此咱们就不须要在手动作这个事情了。
在咱们以前的代码中,咱们定义了一个购物车提交事件,它继承自事件基类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; } }
完成事件定义部分的修改以后,咱们还须要重构事件处理器的代码。
在以前的代码中,针对购物车提交事件,咱们定义了两个处理器,一个是建立订单处理器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的中介者模式实现,它虽然只支持进程内的消息传递,可是却能够简化事件发布/订阅代码,帮助实现业务逻辑代码的解耦,你能够本身试一试。