事件匯流排 是將訊息從傳送方傳輸到接收方的中介. 它在物件,服務和應用程式之間提供了一種鬆散耦合的通訊方式.
ABP框架提供了兩種事件匯流排型別;
釋出事件以下介紹了兩種釋出本地事件的方法.
ILocalEventBus
可以注入 ILocalEventBus 並且使用釋出本地事件.
示例: 產品的存貨數量發生變化時釋出本地事件
using System;using System.Threading.Tasks;using Volo.Abp.DependencyInjection;using Volo.Abp.EventBus.Local;namespace AbpDemo{ public class MyService : ITransientDependency { private readonly ILocalEventBus _localEventBus; public MyService(ILocalEventBus localEventBus) { _localEventBus = localEventBus; } public virtual async Task ChangeStockCountAsync(Guid productId, int newCount) { //TODO: IMPLEMENT YOUR LOGIC... //PUBLISH THE EVENT await _localEventBus.PublishAsync( new StockCountChangedEvent { ProductId = productId, NewCount = newCount } ); } }}
PublishAsync 方法需要一個引數:事件物件,它負責保持與事件相關的資料,是一個簡單的普通類:
using System;namespace AbpDemo{ public class StockCountChangedEvent { public Guid ProductId { get; set; } public int NewCount { get; set; } }}
即使你不需要傳輸任何資料也需要建立一個類(在這種情況下為空類).
實體/聚合根類
實體不能透過依賴注入注入服務,但是在實體/聚合根類中釋出本地事件是非常常見的.
示例: 在聚合根方法內釋出本地事件
using System;using Volo.Abp.Domain.Entities;namespace AbpDemo{ public class Product : AggregateRoot<Guid> { public string Name { get; set; } public int StockCount { get; private set; } private Product() { } public Product(Guid id, string name) : base(id) { Name = name; } public void ChangeStockCount(int newCount) { StockCount = newCount; //ADD an EVENT TO BE PUBLISHED AddLocalEvent( new StockCountChangedEvent { ProductId = Id, NewCount = newCount } ); } }}
AggregateRoot 類定義了 AddLocalEvent 來新增一個新的本地事件,事件在聚合根物件儲存(建立,更新或刪除)到資料庫時釋出.
如果實體釋出這樣的事件,以可控的方式更改相關屬性是一個好的實踐,就像上面的示例一樣 - StockCount只能由保證釋出事件的 ChangeStockCount 方法來更改.
IGeneratesDomainEvents 介面
實際上新增本地事件並不是 AggregateRoot 類獨有的. 你可以為任何實體類實現 IGeneratesDomainEvents. 但是 AggregateRoot 預設實現了它以簡化你的工作.
不建議為不是聚合根的實體實現此介面,因為它可能不適用於此類實體的某些資料庫提供程式. 例如它適用於EF Core,但不適用於MongoDB.
它是如何實現的?
呼叫 AddLocalEvent 不會立即釋出事件. 當你將更改儲存到資料庫時釋出該事件;
對於 EF Core, 它在 DbContext.SaveChanges 中釋出.對於 MongoDB, 它在你呼叫倉儲的 InsertAsync, UpdateAsync 或 DeleteAsync 方法時發由 (因為MongoDB沒有更改跟蹤系統).訂閱事件一個服務可以實現 ILocalEventHandler<TEvent> 來處理事件.
示例: 處理上面定義的StockCountChangedEvent
using System.Threading.Tasks;using Volo.Abp.DependencyInjection;using Volo.Abp.EventBus;namespace AbpDemo{ public class MyHandler : ILocalEventHandler<StockCountChangedEvent>, ITransientDependency { public async Task HandleEventAsync(StockCountChangedEvent eventData) { //TODO: your code that does somthing on the event } }}
MyHandler 由ABP框架自動發現,並在發生 StockCountChangedEvent 事件時呼叫 HandleEventAsync.
事件可以由0個或多個處理程式訂閱.一個事件處理程式可以訂閱多個事件,但是需要為每個事件實現 ILocalEventHandler<TEvent> 介面.事件處理程式類必須註冊到依賴注入(DI),示例中使用了 ITransientDependency.
如果您執行資料庫操作並在事件處理程式中使用倉儲,那麼您可能需要建立一個工作單元,因為一些儲存庫方法需要在活動的工作單元中工作. 確保處理方法設定為 virtual,併為該方法新增一個 [UnitOfWork] attribute. 或者手動使用 IUnitOfWorkManager 建立一個工作單元範圍.
事務和異常行為當一個事件釋出,訂閱的事件處理程式將立即執行.所以;
如果處理程式丟擲一個異常,它會影響釋出該事件的程式碼. 這意味著它在 PublishAsync 呼叫上獲得異常. 因此如果你想隱藏錯誤,在事件處理程式中使用try-catch.如果在一個工作單元範圍內執行的事件釋出的程式碼,該事件處理程式也由工作單元覆蓋. 這意味著,如果你的UOW是事務和處理程式丟擲一個異常,事務會回滾.預定義的事件釋出實體建立,更新,刪除事件是常見的操作. ABP框架為所有的實體自動釋出這些事件. 你只需要訂閱相關的事件.
示例: 訂閱使用者建立事件
using System.Threading.Tasks;using Microsoft.AspNetCore.Identity;using Volo.Abp.DependencyInjection;using Volo.Abp.Domain.Entities.Events;using Volo.Abp.EventBus;namespace AbpDemo{ public class MyHandler : ILocalEventHandler<EntityCreatedEventData<IdentityUser>>, ITransientDependency { public async Task HandleEventAsync(EntityCreatedEventData<IdentityUser> eventData) { var userName = eventData.Entity.UserName; var email = eventData.Entity.Email; //... } }}
這個類訂閱 EntityCreatedEventData<IdentityUser>,它在使用者建立後釋出. 你可能需要向新使用者傳送一封"歡迎"電子郵件.
這些事件有兩種型別:過去時態的事件和進行時態的事件.
用於過去時態事件
當相關工作單元完成且實體更改成功儲存到資料庫時,將釋出帶有過去時態的事件. 如果在這些事件處理程式上丟擲異常,則無法回滾事務,因為事務已經提交.
事件型別;
EntityCreatedEventData<T> 當實體建立成功後釋出.EntityUpdatedEventData<T> 當實體更新成功後釋出.EntityDeletedEventData<T> 當實體刪除成功後釋出.EntityChangedEventData<T> 當實體建立,更新,刪除後釋出. 如果你需要監聽任何型別的更改,它是一種快捷方式 - 而不是訂閱單個事件.用於進行時態事件
帶有進行時態的事件在完成事務之前釋出(如果資料庫事務由所使用的資料庫提供程式支援). 如果在這些事件處理程式上丟擲異常,它會回滾事務,因為事務還沒有完成,更改也沒有儲存到資料庫中.
事件型別;
EntityCreatingEventData<T> 當新實體儲存到資料庫前釋出.EntityUpdatingEventData<T> 當已存在實體更新到資料庫前釋出.EntityDeletingEventData<T> 刪除實體前釋出.EntityChangingEventData<T> 當實體建立,更新,刪除前釋出. 如果你需要監聽任何型別的更改,它是一種快捷方式 - 而不是訂閱單個事件.它是如何實現的?在將更改儲存到資料庫時釋出預構建事件;
對於 EF Core, 他們在 DbContext.SaveChanges 釋出.對於 MongoDB, 在你呼叫倉儲的 InsertAsync, UpdateAsync 或 DeleteAsync 方法釋出(因為MongoDB沒有更改追蹤系統).