.NET Core开发实战(第35课:MediatR:让领域事件处理更加优雅)--学习笔记
35 | MediatR:讓領(lǐng)域事件處理更加優(yōu)雅
核心對(duì)象
IMediator
INotification
INotificationHandler
這兩個(gè)與之前的 Request 的行為是不一樣的,接下來看一下代碼
internal class MyEvent : INotification {public string EventName { get; set; } }internal class MyEventHandler : INotificationHandler<MyEvent> {public Task Handle(MyEvent notification, CancellationToken cancellationToken){Console.WriteLine($"MyEventHandler執(zhí)行:{notification.EventName}");return Task.CompletedTask;} }internal class MyEventHandlerV2 : INotificationHandler<MyEvent> {public Task Handle(MyEvent notification, CancellationToken cancellationToken){Console.WriteLine($"MyEventHandlerV2執(zhí)行:{notification.EventName}");return Task.CompletedTask;} } //await mediator.Send(new MyCommand { CommandName = "cmd01" }); await mediator.Publish(new MyEvent { EventName = "event01" });之前 mediator 使用了 Send 的方式來處理 Command,它還有一個(gè)方法 Publish,這個(gè)方法的入?yún)⑹且粋€(gè) INotification
啟動(dòng)程序,輸出如下:
MyEventHandler執(zhí)行:event01 MyEventHandlerV2執(zhí)行:event01與之前的 IRequest 不同的是,INotification 是可以注冊(cè)多個(gè) Handler 的,它是一個(gè)一對(duì)多的關(guān)系,借助它就可以對(duì)領(lǐng)域事件定義多個(gè)處理器來處理
接著看一下之前云服務(wù)的代碼
public async Task<bool> SaveEntitiesAsync(CancellationToken cancellationToken = default) {var result = await base.SaveChangesAsync(cancellationToken);await _mediator.DispatchDomainEventsAsync(this);return true; }之前在 IUnitOfWork 定義的時(shí)候講過一個(gè)發(fā)送領(lǐng)域事件的方法 DispatchDomainEventsAsync,看一下這個(gè)方法的定義
static class MediatorExtension {public static async Task DispatchDomainEventsAsync(this IMediator mediator, DbContext ctx){var domainEntities = ctx.ChangeTracker.Entries<Entity>().Where(x => x.Entity.DomainEvents != null && x.Entity.DomainEvents.Any());var domainEvents = domainEntities.SelectMany(x => x.Entity.DomainEvents).ToList();domainEntities.ToList().ForEach(entity => entity.Entity.ClearDomainEvents());foreach (var domainEvent in domainEvents)await mediator.Publish(domainEvent);} }可以看到這里是將所有的實(shí)體內(nèi)的領(lǐng)域事件全部都查找出來,然后通過 mediator 的 Publish 發(fā)送領(lǐng)域事件,具體的領(lǐng)域事件的處理注冊(cè)在 mediator 里面,這里定義了一個(gè) OrderCreatedDomainEventHandler
public class OrderCreatedDomainEventHandler : IDomainEventHandler<OrderCreatedDomainEvent> {ICapPublisher _capPublisher;public OrderCreatedDomainEventHandler(ICapPublisher capPublisher){_capPublisher = capPublisher;}public async Task Handle(OrderCreatedDomainEvent notification, CancellationToken cancellationToken){await _capPublisher.PublishAsync("OrderCreated", new OrderCreatedIntegrationEvent(notification.Order.Id));} }它繼承自 IDomainEventHandler,而 IDomainEventHandler 繼承自 INotificationHandler
public interface IDomainEventHandler<TDomainEvent> : INotificationHandler<TDomainEvent>where TDomainEvent : IDomainEvent {//這里我們使用了INotificationHandler的Handle方法來作為處理方法的定義Task Handle(TDomainEvent domainEvent, CancellationToken cancellationToken); }這也就是為什么 IDomainEventHandler 會(huì)識(shí)別到 DomainEvent 并且進(jìn)行處理,同樣的在定義 DomainEvent 的時(shí)候,也需要標(biāo)識(shí)它是一個(gè) DomainEvent
public class OrderCreatedDomainEvent : IDomainEvent {public Order Order { get; private set; }public OrderCreatedDomainEvent(Order order){this.Order = order;} }而 DomainEvent 實(shí)際上也是繼承自 INotification
public interface IDomainEvent : INotification { }這也就意味著 EventHandler 可以正確的識(shí)別到對(duì)應(yīng)的 Event 并且進(jìn)行處理,這都是 MediatR 的核心能力
領(lǐng)域事件都是定義在 event 目錄下,與領(lǐng)域模型定義在一起,所有的領(lǐng)域事件都繼承 DomainEvent,分布于這個(gè)目錄
領(lǐng)域事件的處理 Handler 都定義在 Application 應(yīng)用層的 Application 下面的 DomainEventHandlers 目錄下面
這樣的好處是事件的定義與事件的處理是分開的,并且非常的明確知道有哪些領(lǐng)域事件,有哪些領(lǐng)域事件的處理程序
關(guān)于 MediatR 再補(bǔ)充一部分內(nèi)容,在 TransactionBehavior 內(nèi)可以看到這個(gè)類實(shí)際上繼承自 IPipelineBehavior
namespace MediatR {public interface IPipelineBehavior<in TRequest, TResponse>{Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next);} }這個(gè)接口的作用是在命令或者事件處理的之前或者之后插入邏輯,它的執(zhí)行的方式有點(diǎn)像中間件的方式,在 Handler 的入?yún)⒗锩嬗幸粋€(gè) next 的參數(shù),就是指 CommandHandler 或者 EventHandler 的執(zhí)行的邏輯,在這里就可以決定 Handler 的具體執(zhí)行之前或者之后,插入一些邏輯
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next) {var response = default(TResponse);var typeName = request.GetGenericTypeName();try{// 首先判斷當(dāng)前是否有開啟事務(wù)if (_dbContext.HasActiveTransaction){return await next();}// 定義了一個(gè)數(shù)據(jù)庫(kù)操作執(zhí)行的策略,比如說可以在里面嵌入一些重試的邏輯,這里創(chuàng)建了一個(gè)默認(rèn)的策略var strategy = _dbContext.Database.CreateExecutionStrategy();await strategy.ExecuteAsync(async () =>{Guid transactionId;using (var transaction = await _dbContext.BeginTransactionAsync())using (_logger.BeginScope("TransactionContext:{TransactionId}", transaction.TransactionId)){_logger.LogInformation("----- 開始事務(wù) {TransactionId} ({@Command})", transaction.TransactionId, typeName, request);response = await next();// next 實(shí)際上是指我們的后續(xù)操作,這里的模式有點(diǎn)像之前講的中間件模式_logger.LogInformation("----- 提交事務(wù) {TransactionId} {CommandName}", transaction.TransactionId, typeName);await _dbContext.CommitTransactionAsync(transaction);transactionId = transaction.TransactionId;}});return response;}catch (Exception ex){_logger.LogError(ex, "處理事務(wù)出錯(cuò) {CommandName} ({@Command})", typeName, request);throw;} }這里實(shí)現(xiàn)里在執(zhí)行命令之前判斷事務(wù)是否開啟,如果事務(wù)開啟的話繼續(xù)執(zhí)行后面的邏輯,如果事務(wù)沒有開啟,先開啟事務(wù),再執(zhí)行后面的邏輯
總結(jié)
以上是生活随笔為你收集整理的.NET Core开发实战(第35课:MediatR:让领域事件处理更加优雅)--学习笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C#中的9个“黑魔法”与“骚操作”
- 下一篇: (译)创建.NET Core多租户应用程