文档目录

本节内容:

  • EventBus

    • 注入 IEventBus
    • 获取默认实例
  • 定义事件
    • 预定义事件

      • 处理完异常
      • 实体修改
  • 触发事件
  • 处理事件
    • 处理基类事件
    • 处理程序异常
    • 处理多个事件
  • 处理程序注册
    • 自动
    • 手动
  • 反注册

在C#里,一个类可以定义自己的事件,然后其它类可以注册它,当某些事情发生时,接收到通知。这对于桌面应用或单机的Windows服务非常有用。但是,对于一个Web应用,它就有点问题,因为对象在一个web请求里创建,并且它们生命周期都很短。所以就难于注册一些类事件,同时,直接注册另一个类的事件,也使得类之间更加藕合。

领域事件一般用来解藕业务逻辑和在应用里发生重要领域修改时发出通知。

EventBus

EventBus是一个单例对象,被所有类触发事件或处理事件时共享。为使用事件总线,你先要引用它,有两种方式。

注入 IEventBus

你可以用依赖注入获取一个IEventBus的引用,这儿我们使用属性注入模式:

public class TaskAppService : ApplicationService
{public IEventBus EventBus { get; set; }
public TaskAppService(){EventBus = NullEventBus.Instance;}
}

在注入事件总线上,属性注入比构造器注入更合适。你的类可以没有事件总线,NullEventBus实现了空对象模式,当你调用它的方法时,方法里什么也不做。

获取默认实例

如果你不能注入它,可以直接使用EventBus.Default。它是全局的事件总线,使用方式如下所示:

EventBus.Default.Trigger(...); //trigger an event

在任何可能的地方都不建议直接使用EventBus.Default,因为它难于单元测试。

定义事件

在触发一个事件前,你首先要定义它,通过一个继承自EventData的类来表现一个事件。假设当一个任务完成后我们想触发一个事件:

public class TaskCompletedEventData : EventData
{public int TaskId { get; set; }
}

这个类包含处理事件类所需要的属性,EventData类定义了EventSource(事件源,哪个对象触发了事件)和EventTime(何时触发的)属性。

预定义事件

处理完异常

ABP定义了AbpHandledExceptionData,并当ABP自动处理任何异常时,会触发这个事件,这在你想了解更多异常信息时尤其有用(尽管ABP自动记录了所有异常)。你可以注册这个事件,当异常发生时,发出通知。

实体修改

为实体修改提供了泛型的事件:EntityCreationEventData<Tentity>、EntityCreatedEventData<TEntity>、EntityUpdatingEventData<TEntity>、EntityUpdateEventData<TEntity>、EntityDeletingEventData<TEntity>和EntityDeletedEventData<TEntity>,同样也有EntityChangingEventData<TEntity>和EntityChangedEventData<TEntity>,修改可以是插入、更新或删除。

“ing”事件(例如EntityUpdating)在保存修改(SaveChanges)前触发,所以你可以在这些事件里,通过抛出异常,促使工作单元回滚,阻止操作)。“ed”事件(例如EntityUpdated)在保存修改之后被触发,也就没有机会让工作单元回滚了。

实体修改事件定义在Abp.Events.Bus.Entities命名空间里,并在插入、更新或删除实体时,被ABP自动触发。如果你有一个Person实体,你可以注册EntityCreatedEventData<Person>,当一个新的Person创建并插入到数据库后,就可以收到通知。这些事件也支持继承,如果你有一个继承自Person的Student类,并且注册了EntityCreatedEventData<Person>,当一个Person或Student被插入后,你也会收到通知。

触发事件

触发一个事件很简单:

public class TaskAppService : ApplicationService
{public IEventBus EventBus { get; set; }    public TaskAppService(){EventBus = NullEventBus.Instance;}public void CompleteTask(CompleteTaskInput input){//TODO: complete the task on database...EventBus.Trigger(new TaskCompletedEventData {TaskId = 42});}
}

Trigger方法有几个重载:

EventBus.Trigger<TaskCompletedEventData>(new TaskCompletedEventData { TaskId = 42 }); //Explicitly declare generic argument
EventBus.Trigger(this, new TaskCompletedEventData { TaskId = 42 }); //Set 'event source' as 'this' EventBus.Trigger(typeof(TaskCompletedEventData), this, new TaskCompletedEventData { TaskId = 42 }); //Call non-generic version (first argument is the type of the event class)

触发事件的另一个方法是:使用AggregateRoot类的DomainEvents集合(查看实体文档的相关小节)。

处理事件

为处理一个事件,你应该实现IEventHandler<T>接口,如下所示:

public class ActivityWriter : IEventHandler<TaskCompletedEventData>, ITransientDependency
{public void HandleEvent(TaskCompletedEventData eventData){WriteActivity("A task is completed by id = " + eventData.TaskId);}
}

IEventHandler定义了HandleEvent方法,并像上面那样实现它。

EventBus被整合到依赖注入系统里,如我们上面那样实现ITransientDependency,当一个TaskCompleted事件发生后,它创建一个新的ActivityWriter实例,并调用它的HandleEvent方法,然后释放它,更多信息查看依赖注入。

处理基类事件

EventBus支持事件的继承,例如:你可以创建一个TaskEventData和两个子类:TaskCompletedEventData和TaskCreatedEventData:

public class TaskEventData : EventData
{public Task Task { get; set; }
}public class TaskCreatedEventData : TaskEventData
{public User CreatorUser { get; set; }
}public class TaskCompletedEventData : TaskEventData
{public User CompletorUser { get; set; }
}

然后你可以实现IEventhandler<TaskEventData>来处理这两种事件:

public class ActivityWriter : IEventHandler<TaskEventData>, ITransientDependency
{public void HandleEvent(TaskEventData eventData){if (eventData is TaskCreatedEventData){//...
        }else if (eventData is TaskCompletedEventData){//...
        }}
}

这也就意味着,你可以实现IEventHandler<EventData>来处理应用中的所有事件,你可能不想这样做,但它是可以做到的。

处理程序异常

在处理程序(Handler)抛出一个/一些异常时,Eventbus触发所有Handler事件,如果只有一个处理程序抛出异常,异常会直接被Trigger方法抛出,如果多个处理程序抛出异常,EventBus只为它们抛出一个AggregateException异常。

处理多个事件

在一个处理程序里你可以处理多个事件,此次,你应该为每个事件实现IEventHandler<T>,例如:

public class ActivityWriter : IEventHandler<TaskCompletedEventData>,
    IEventHandler<TaskCreatedEventData>, ITransientDependency
{public void HandleEvent(TaskCompletedEventData eventData){//TODO: handle the event...
    }public void HandleEvent(TaskCreatedEventData eventData){//TODO: handle the event...
    }
}

处理程序注册

为处理事件,我们必须在事件总线里注册处理程序。

自动

ABP找到所有实现IEVentHandler的类并注册到依赖注入(例如:通过实现ITransientDependency,如上面的示例),然后ABP自动把它们注册到事件总线,当一个事件发生,ABP使用依赖注入得到处理程序的引用,并在事件处理后释放该引用。在ABP里,这是使用事件总线的推荐的方式。

手动

可以手动注册事件,但要小心使用。在一个web应用里,事件注册应当中应用启动里完成。在一个Web请求里,注册事件不是一个好的方式,因为注册类请完成后继续注册,并为每个请求重新注册,这可能会引起问题,因为注册类多次被调用。同时要记住,手动注册不使用依赖注入系统。

事件总线的Register方法有几个重载,最简单的是接受一个委托(或lambda):

EventBus.Register<TaskCompletedEventData>(eventData =>{WriteActivity("A task is completed by id = " + eventData.TaskId);});

“任务完成”事件发生后,这个lambda方法就会被调用。第二个是接受一个实现了IEventHantler<T>的对象:

EventBus.Register<TaskCompletedEventData>(new ActivityWriter());

同样是为事件调用ActivityWriter实例。第三个重载接受两个泛型参数:

EventBus.Register<TaskCompletedEventData, ActivityWriter>();

此次,事件总线为每个事件创建一个新的ActivityWriter,如果它是disposable(可释放),并调用ActivityWriter.Dispose方法。

最后,你可以注册一个事件处理程序工作,负责处理程序的创建。一个处理程序工厂有两个方法:GetHandler和ReleaseHandler。例如:

public class ActivityWriterFactory : IEventHandlerFactory
{public IEventHandler GetHandler(){return new ActivityWriter();}public void ReleaseHandler(IEventHandler handler){//TODO: release/dispose the activity writer instance (handler)
    }
}

还有一个特殊的工厂类IocHandlerFactory。它使用依赖注入系统来创建/释放处理程序。ABP在自动注册里也使用这个类,所以,如果你想使用依赖注入系统,直接使用之前定义的自动注册。

反注册

当你向事件总线注册后,想反注册事件,最简单的方式就是释放Register方法返回的值,例如:

//Register to an event...
var registration = EventBus.Register<TaskCompletedEventData>(eventData => WriteActivity("A task is completed by id = " + eventData.TaskId) );//Unregister from event
registration.Dispose();

当然,其它地方或其它某个时刻,都可能需要反注册,你可以保存注册对象并在需要时释放它。Register方法的所有重载都返回一个可释放的对象给事件。

EventBus也提供了Unregister方法,使用示例:

//Create a handler
var handler = new ActivityWriter();//Register to the event
EventBus.Register<TaskCompletedEventData>(handler);//Unregister from event
EventBus.Unregister<TaskCompletedEventData>(handler);

它也提供了重载来反注册委托和工厂。反注册处理程序对象必须是注册时的对象。

最后,EventBus提供了一个UnregisterAll<T>()方法,它反注册一个事件的所有处理程序,UnregisterAll()方法反注册所有事件的所有处理程序。

转载于:https://www.cnblogs.com/kid1412/p/5998934.html

ABP框架 - 领域事件(EventBus)相关推荐

  1. 从ASP.NET Boilerplate v5 +到ABP框架的迁移

    文章目录 介绍 我应该迁移吗? 那ASP.NET Zero呢? ASP.NET MVC 5.x项目 迁移进度 创建解决方案 关于预构建模块 领域层 聚合根和实体 复合主键 聚合根 迁移现有实体 文献资 ...

  2. DDD~领域事件中使用分布式事务

    对于一个聚合来说,它可能会被附加很多事件,这里我们叫它领域事务,因为一个聚会我们可以把它理解成一个领域,一个业务.对于领域事件不清楚的同学可以看看我的这篇文章<DDD~领域事件与事件总线> ...

  3. ABP官方文档翻译 3.7 领域事件(事件总线)

    领域事件(事件总线) 事件总线 注入IEventBus 获取默认实例 定义事件 预定义事件 处理异常 实体更改 触发事件 处理事件 处理基础事件 处理者异常 处理多个事件 注册处理者 自动 手动 取消 ...

  4. 如何使用ABP框架(2)三层架构与领域驱动设计的对比

    本文来自长沙.NET技术社区,原创:邹溪源.全文共有8500字,读完需耗时10分钟. 题图来自@pixabay 简述 上一篇简述了ABP框架中的一些基础理论,包括ABP前后端项目的分层结构,以及后端项 ...

  5. ABP入门系列(19)——使用领域事件

    1.引言 最近刚学习了下DDD中领域事件的理论知识,总的来说领域事件主要有两个作用,一是解耦,二是使用领域事件进行事务的拆分,通过引入事件存储,来实现数据的最终一致性.若想了解DDD中领域事件的概念, ...

  6. c/s三层结构信息系统的三个层次_如何使用ABP框架(2)三层架构与领域驱动设计的对比...

    本文来自长沙.NET技术社区,原创:邹溪源.全文共有8500字,读完需耗时10分钟. 题图来自@pixabay 简述 上一篇简述了ABP框架中的一些基础理论,包括ABP前后端项目的分层结构,以及后端项 ...

  7. Android中使用EventBus事件发布/订阅框架实现事件传递

    场景 EventBus EventBus是一种用于Android的事件发布-订阅总线.它简化了应用程序内各个组件之间进行通信的复杂度,尤其是碎片之间进行通信的问题,可以避免由于使用广播通信而带来的诸多 ...

  8. 老周的ABP框架系列教程

    老周的ABP框架系列教程 -- 一.框架理论初步学习   1. ABP框架的来源与作用简介 1.1  简介 1.1.1       ABP框架全称为"ASP.NET Boilerplate ...

  9. ABP框架与基础组件介绍

    ABP:基于DDD的现代ASP.NET开发框架 1:ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称. 2:一个用最佳实践和流行 ...

最新文章

  1. 下一代微服务!ServiceMesh的2018年度总结 | 万字雄文
  2. 计算机的组成_计算机网络的组成和分类
  3. 电脑不能打字_宝妈、学生、上班族手机兼职——打字录入
  4. UE4学习-第三人称游戏的AI巡逻
  5. WPF 透明窗口在桌面上放虫子。。。
  6. 5799元!OPPO Find X5 Pro天玑版即将开卖:性能比肩骁龙8
  7. 尝试从远程计算机访问Web服务不显示调用按钮
  8. 动态文本_(302期)【动态】|| 立足相同文本,描绘不同风景 ——工作室开展“同课异构”活动...
  9. C语言 —— 合并两个有序数组
  10. 贵就好?中消协买20款扫地机器人,艾罗伯特这款噪音大!
  11. Windows邮箱登录QQ邮箱
  12. 以HT82K629B为主控的程序员专用CV键盘(无需代码烧写)
  13. 解决can't resolve the symbol 'R'方法(转载)
  14. 如何取消qq的“小世界”功能
  15. CentOS 6.8安装Docker V1.0
  16. 18位身份证号验证算法的原理以及C#实现和在管理系统的应用
  17. VS编译失败,找不到源文件!!!
  18. Android Scroller的使用
  19. 入门级蛋白质结构查看PyMol的使用——用PyMol制作视频movie
  20. 问到的知识(蓝色板儿砖的教学)

热门文章

  1. 腾讯云Linux服务器搭建(三) Mysql 8.0.11的安装和设置
  2. 解决jpgraph汉字乱码的两种方法
  3. 怎么设置uboot从u盘启动linux,rt5350使用uboot从u盘启动linux成功含从u盘加载镜像与rootfs...
  4. 如何关闭或启动mysql服务
  5. 关于研发效能提升的思考
  6. Scala知识点总结(上半部分)
  7. 与sscanf函数的一见钟情(真是相见恨晚!)
  8. SmartPTT、SmartICS 工业产品存在多个严重漏洞,影响全球90国
  9. 【论文笔记】具有反馈控制的自主优化
  10. vue导出导入csv文件(无需插件)