在上一篇中,我們學習介紹了Abp本地事件的基礎應用,但都沒有涉及到數據庫層面的執行。
在數據操作上,abp也提供了很好的事件處理機制,針對數據的增刪改操作默認發佈了事件,我們只需要訂閱對應事件即可。
同時,在上一篇中,我們也提供了abp的訂閱是非原子性的,也就是訂閱端如果處理失敗,是沒有事務回滾或者重試的機制的。
那麼對應到數據處理,我們就需要在數據的變更前、後做對應處理,比如等前置事件變更到數據庫成功後,才處理訂閱事件。
對此,abp也提供了對應的事件。
我們定義了一個學生Student類,還有一個積分Score類,當執行insert學生前和後,我們就寫入積分。
我們在Student實體中定義了ChangeScoreCount方法,在該方法發佈事件:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; using Volo.Abp.Domain.Entities; namespace EventBus { public class Student:AggregateRoot<Guid> { [DisplayName("姓名")] public string Name { get; set; } [DisplayName("年齡")] public int Age { get; set; } public void ChangeScoreCount(int score) { //ADD an EVENT TO BE PUBLISHED AddLocalEvent( new ScoreCountChangedEvent { StudentId = this.Id, Score = score } ); } } public class ScoreCountChangedEvent { public Guid StudentId { get; set; } public int Score { get; set; } } }
如果我們的實體繼承了AggregateRoot 聚合根,那在實體類中可使用AddLocalEvent 方法進行事件的發佈,否則就使用上一篇中講述的ILocalEventBus進行發佈即可。
AggregateRoot
類定義了 AddLocalEvent
來添加一個新的本地事件,事件在聚合根對象保存(創建,更新或刪除)到數據庫時發佈.
然後,我們在應用層的StudentService類中定義student的寫入方法,並執行ChangeScoreCount
using Volo.Abp.Application.Services; using Volo.Abp.Domain.Repositories; namespace EventBus { public class StudentService : ApplicationService,IStudentService { public IRepository<Student, Guid> studentRepository { get; set; } public async Task<bool> InsertAsync(string name, int age) { var model = new Student() { Name = name, Age = age }; await studentRepository.InsertAsync(model); model.ChangeScoreCount(new Random().Next()); //調用積分變更方法,執行事件發佈 return true; } } }
在home控制器中定義方法調用student的InsertAsync方法
public async Task<IActionResult> Test() { await studentService.InsertAsync("張三", new Random().Next()); return Ok("Ok"); }
接下來,我們定義訂閱函數,新建一個StudentHandler類來訂閱學生的事件
using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities.Events; using Volo.Abp.EventBus; namespace EventBus.Models { public class StudentHandler : ILocalEventHandler<ScoreCountChangedEvent>,ILocalEventHandler<EntityCreatedEventData<Student>>, ILocalEventHandler<EntityCreatingEventData<Student>> , ITransientDependency { public IScoreService scoreService { get; set; } public async Task HandleEventAsync(ScoreCountChangedEvent eventData) { //TODO: your code that does somthing on the event await scoreService.AddScoreAsync(eventData.StudentId, eventData.Score); Console.WriteLine("學生積分訂閱完成"); } //當實體寫入數據庫之後執行 public async Task HandleEventAsync(EntityCreatedEventData<Student> eventData) { await scoreService.AddScoreAsync(eventData.Entity.Id, 10); Console.WriteLine("實體寫入成功後執行"); } /// <summary> /// 實體寫入數據庫之前執行 /// </summary> /// <param name="eventData"></param> /// <returns></returns> public async Task HandleEventAsync(EntityCreatingEventData<Student> eventData) { await scoreService.AddScoreAsync(eventData.Entity.Id, 10); Console.WriteLine("實體寫入前執行"); } } }
這裏,我們總共訂閱了3個事件
1:ScoreCountChangedEvent事件,是我們在StudentService方法中主動發佈的事件
2:EntityCreatedEventData<Student>事件,是abp默認發佈的,在實體寫入數據庫成功之後發佈的事件
3:EntityCreatingEventData<Student>事件,是abp默認發佈的,在實體寫入數據庫之前發佈的事件。
這裏面關鍵的就是在進入數據前後分別都有對應的事件可以訂閱處理。
下面,我們運行起來,看下執行順序
通過運行後的結果,可以看到實體寫入前後的順序是遵循規則來的。
abp還發布了其他事件:
用過去時態事件
當相關工作單元完成且實體更改成功保存到數據庫時,將發佈帶有過去時態的事件. 如果在這些事件處理程序上拋出異常,則無法回滾事務,因爲事務已經提交.
事件類型;
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沒有更改追蹤系統).
以上兩篇文章,就是abp在本地事件發佈的相關應用分享。
源碼:https://gitee.com/fei686868/EventBus
更多分享,請大家關注我的個人公衆號: