ABP-VNEXT 学习笔记(六)事件总线--本地事件总线2
在上一篇中,我们学习介绍了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
更多分享,请大家关注我的个人公众号: