ABP理论学习之事件总线和领域事件
本篇目录
在C#中,我们可以在一个类中定义自己的事件,而其他的类可以注册该事件,当某些事情发生时,可以通知到该类。这对于桌面应用或者独立的windows服务来说是非常有用的。但对于一个web应用来说是有点问题的,因为对象都是在web请求中创建的,而且这些对象生命周期都很短,因而注册某些类的事件是很困难的。此外,注册其他类的事件会使得类紧耦合。
领域事件用于解耦并重复利用应用中的逻辑。
事件总线###
事件总线是被所有触发并处理事件的其他类共享的单例对象。要使用事件总线,首先应该获得它的一个引用。下面有两种方法来处理:
创建默认实例
你可以直接使用 EventBus.Default。这是全局的事件总线,用法如下所示:
EventBus.Default.Trigger(...); //触发一个事件
注入IEventBus
不直接使用EventBus.Default,你也可以使用依赖注入来获得IEventBus的引用。这有利于单元测试。这里我们使用属性注入模式:
public class TaskAppService : ApplicationService
{
public IEventBus EventBus { get; set; }
public TaskAppService()
{
EventBus = NullEventBus.Instance;
}
}
对于注入事件总线这件事,属性注入比构造函数注入更合适。这样,你的类离开事件总线还能工作。NullEventBus实现了null对象模式。当你调用上面的构造函数时,实际上啥都没做。
定义事件###
触发事件之前,应该先要定义该事件。事件是使用派生自EventData的类来表示的。假设我们想当一个任务task完成时触发一个事件:
public class TaskCompletedEventData : EventData
{
public int TaskId { get; set; }
}
该类包含了类处理事件需要的属性。EventData类定义了 EventSource(事件源)和 EventTime(事件触发时间)属性。
预定义事件
ABP定义了AbpHandleExceptionData,当自动处理任何异常时都会触发这个事件。如果你想要获得更多的关于异常的信息(甚至ABP会自动记录所有的异常),那么这是特别有用的。注册这个事件之后,异常发生时就会通知你。
对于实体的更改也有泛型的事件数据类:EntityCreatedEventData,EntityUpdateEventData和 EntityDeletedEventData。它们都定义在 Abp.Event.Bus.Entities命名空间中。当一个实体插入,更新或者删除时,ABP会自动地触发这些事件。比如,如果你有一个Person实体,将它注册到EntityCreatedEventData,那么当创建的新的Person实体对象插入数据库时,会收到通知。这些事件也支持继承。如果Student类派生自Person类,而且你将它注册到EntityCreatedEventData,那么当一个Person或者Student插入时,你会收到通知。
触发事件###
触发一个事件很简单,如下所示:
public class TaskAppService : ApplicationService
{
public IEventBus EventBus { get; set; }
public TaskAppService()
{
EventBus = NullEventBus.Instance;
}
public void CompleteTask(CompleteTaskInput input)
{
//TODO: 完成task的数据库操作...
EventBus.Trigger(new TaskCompletedEventData {TaskId = 42});
}
}
下面是Trigger方法的一些重载:
EventBus.Trigger<TaskCompletedEventData>(new TaskCompletedEventData { TaskId = 42 }); //显示声明为泛型参数
EventBus.Trigger(this, new TaskCompletedEventData { TaskId = 42 }); //将 '事件源'设置为'this'
EventBus.Trigger(typeof(TaskCompletedEventData), this, new TaskCompletedEventData { TaskId = 42 });//调用非泛型版本(第一个参数是事件类的类型)
处理事件###
要处理一个事件,应该要实现IEventHandler接口,如下所示:
public class ActivityWriter : IEventHandler<TaskCompletedEventData>, ITransientDependency
{
public void HandleEvent(TaskCompletedEventData eventData)
{
WriteActivity("A task is completed by id = " + eventData.TaskId);
}
}
事件总线(EventBus)已经集成到ABP的依赖注入系统中。正如上面实现ITransientDependency一样,当TaskCompleted事件发生时,它会创建ActivityWriter类的一个新实例,然后调用HandleEvent方法,最后释放它。更多知识请查看依赖注入。
处理基事件
事件总线支持事件的继承。比如,你创建了一个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来处理这两个事件:
public class ActivityWriter : IEventHandler<TaskEventData>, ITransientDependency
{
public void HandleEvent(TaskEventData eventData)
{
if (eventData is TaskCreatedEventData)
{
//...
}
else if (eventData is TaskCompletedEventData)
{
//...
}
}
}
当然了,你可以实现IEventHandler来处理所有你想要处理的事件。
处理多事件
在一个单一的处理句柄中,可以处理多个事件。这时,你应该为每个事件实现IEventHandler。比如:
public class ActivityWriter :
IEventHandler<TaskCompletedEventData>,
IEventHandler<TaskCreatedEventData>,
ITransientDependency
{
public void HandleEvent(TaskCompletedEventData eventData)
{
//TODO: 处理事件...
}
public void HandleEvent(TaskCreatedEventData eventData)
{
//TODO: 处理事件...
}
}
句柄注册###
为了处理事件,我们必须将事件句柄注册给事件总线。
自动
ABP会自动扫描所有的实现了IEventHandler的类,并自动将它们注册到事件总线上。当一个事件发生时,它会使用依赖注入获得该句柄的一个引用,而且在处理该事件之后就会释放该句柄。建议这样使用ABP中的事件总线。
手动
也可能会手动注册到事件,但是要小心使用。在一个web应用中,事件注册应该在应用启动时完成。在web请求时注册到一个事件不是一个好的方法,因为请求完成之后注册的类仍旧是注册的,而且对于每个请求继续再次注册。这可能会对你的应用造成问题,因为注册的类可能被调用多次。而且要记住手动注册不会使用依赖注入系统。
这里有一些事件总线的方法的重载。最简单的一个等待了一个委托(或者一个lambda):
EventBus.Register<TaskCompletedEventData>(eventData =>
{
WriteActivity("A task is completed by id = " + eventData.TaskId);
});
这样,当“一个task完成”事件发生时,这个lambda方法就会调用。第二个等待一个实现了IEventHandler的对象:
EventBus.Register<TaskCompletedEventData>(new ActivityWriter());
事件会调用ActivityWriter的相同实例。该方法也有一个非泛型的重载。另一个重载接受两个泛型的参数:
EventBus.Register<TaskCompletedEventData, ActivityWriter>();
此时,事件总线会为每个事件创建一个新的ActivityWriter。如果它是可释放的,那么会调用ActivityWriter.Dispose方法。
最后,为了处理句柄的创建,你可以注册一个事件句柄工厂。句柄工厂有两个方法:GetHandler和ReleaseHandler。例如:
public class ActivityWriterFactory : IEventHandlerFactory
{
public IEventHandler GetHandler()
{
return new ActivityWriter();
}
public void ReleaseHandler(IEventHandler handler)
{
//TODO:释放ActivityWriter实例 (handler)
}
}
还有一个特殊的工厂类IocHandlerFactory,它可以使用依赖注入系统创建或者释放句柄。ABP在自动注册模式中使用了这个类。因此,如果你想使用依赖注入系统,直接使用自动注册。
取消注册###
手动注册到事件总线时,你可能会在以后想要取消注册该事件。取消注册一个事件的最简单方法是释放该注册方法的返回值。如下所示:
//注册到一个事件...
var registration = EventBus.Register<TaskCompletedEventData>(eventData => WriteActivity("A task is completed by id = " + eventData.TaskId) );
//取消注册事件
registration.Dispose();
当然了,注销注册会在某个地方和某个时间。保留注册对象并在想要取消注册时释放它。所有注册方法的重载都会返回一个可释放的对象以取消注册该事件。
事件总线也提供了Unregister方法。样例用法:
//创建一个句柄
var handler = new ActivityWriter();
//注册到事件
EventBus.Register<TaskCompletedEventData>(handler);
//从事件取消注册
EventBus.Unregister<TaskCompletedEventData>(handler);
它也提供了重载来注销委托和工厂,注销句柄对象必须是之前注册的相同对象。
最后,事件总线提供了UnregisterAll方法来注销一个事件的所有句柄,RegisterAll()方法会注销所有事件的所有句柄。
ABP理论学习之事件总线和领域事件的更多相关文章
- ABP的事件总线和领域事件(EventBus & Domain Events)
http://www.aspnetboilerplate.com/Pages/Documents/EventBus-Domain-Events EventBus EventBus是个单例,获得Even ...
- DDD理论学习系列(9)-- 领域事件
DDD理论学习系列--案例及目录 1. 引言 A domain event is a full-fledged part of the domain model, a representation o ...
- [Abp vNext 源码分析] - 13. 本地事件总线与分布式事件总线 (Rabbit MQ)
一.简要介绍 ABP vNext 封装了两种事件总线结构,第一种是 ABP vNext 自己实现的本地事件总线,这种事件总线无法跨项目发布和订阅.第二种则是分布式事件总线,ABP vNext 自己封装 ...
- ABP官方文档翻译 3.7 领域事件(事件总线)
领域事件(事件总线) 事件总线 注入IEventBus 获取默认实例 定义事件 预定义事件 处理异常 实体更改 触发事件 处理事件 处理基础事件 处理者异常 处理多个事件 注册处理者 自动 手动 取消 ...
- ABP入门系列(19)——使用领域事件
ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1.引言 最近刚学习了下DDD中领域事件的理论知识,总的来说领域事件主要有两个作用,一是解耦,二是 ...
- ABP框架 - 领域事件(EventBus)
文档目录 本节内容: EventBus 注入 IEventBus 获取默认实例 定义事件 预定义事件 处理完异常 实体修改 触发事件 处理事件 处理基类事件 处理程序异常 处理多个事件 处理程序注册 ...
- [Abp 源码分析]九、事件总线
0.简介 事件总线就是订阅/发布模式的一种实现,本质上事件总线的存在是为了降低耦合而存在的. 从上图可以看到事件由发布者发布到事件总线处理器当中,然后经由事件总线处理器调用订阅者的处理方法,而发布者和 ...
- 浅入 ABP 系列(4):事件总线
浅入 ABP 系列(4):事件总线 版权护体作者:痴者工良,微信公众号转载文章需要 <NCC开源社区>同意. 目录 浅入 ABP 系列(4):事件总线 事件总线 关于事件总线 为什么需要这 ...
- Android事件总线
Android中Activity.Service.Fragment之间的相互通信比较麻烦,主要有以下一些方法: (1)使用广播,发送者发出广播,接收者接收广播后进行处理: (2)使用Handler和M ...
随机推荐
- Android Studio一些常用快捷键及快捷键冲突解决
1. 最近在自学Android,也是边看书边写一些Demo,由于知识点越来越多,脑子越来越记不清楚,所以打算写成读书笔记,供以后查看,也算是把自己学到所理解的东西写出来,献丑,如有不对的地方,希望大家 ...
- 公众号第三方平台开发 获取 component_verify_ticket 2015-07-05 10:16 59人阅读 评论(0) 收藏
8.推送component_verify_ticket协议 在公众号第三方平台创建审核通过后,微信服务器会向其"授权事件接收URL"每隔10分钟定时推送component_veri ...
- Android-Menu [使用C# And Java实现]
本篇是对安卓菜单使用编程方式实现,当然可以使用XML的方式完成同样的功能,基本Java和C#写法都是一致的,所以使用XML的方式在本篇中使用Java演示,需要注意的是,对于如果不是VS开发的话,那么资 ...
- AOJ 0558 Cheese【BFS】
在H * W的地图上有N个奶酪工厂,分别生产硬度为1-N的奶酪.有一只吃货老鼠准备从老鼠洞出发吃遍每一个工厂的奶酪.老鼠有一个体力值,初始时为1,每吃一个工厂的奶酪体力值增加1(每个工厂只能吃一次), ...
- Global Translator
Global Translator插件可以把已经通过翻译服务翻译好的内容生成对应语种的“静态”页面,或者说“缓存”起来,这样在一段时间内(可设置)想访问该语种的这 个页面的访客,就可以在不调用翻译服务 ...
- JMeter执行dos命令
1.通过java代码实现 参考Java实现操作dos命令 2.直接通过JMeter内置的beanshell脚本操作 String command = "cmd /c start ping w ...
- 《UML大战需求分析》阅读随笔(一)
UML:Unified Modeling Language(统一建模语言) 作为我专业学科里的一门语言,其目的就是交流,同客户交流,同自己交流. 用图像和文字,详细地讲解将要做的工程的 需求和功能细节 ...
- Unity5中WebGL平台封装的一些技巧
最近在接触unity的WebGL平台,其实这个平台作为Web Player的替代品,已经能满足大部分的开发需求,而且不需要额外的插件支持,确实方便了不少,但开发中依旧遇到了不少问题,在这里记录和共享一 ...
- Mac 不能输入波浪线?
当你发现你的Mac或者mbp不能输入波浪线 , 输出的都是的时候,检查一下这个选项(如下图所示)有没有选中. 如果没有,就勾上它!
- [资料分享]2016 黑马 Java 19期视频+Hadoop大数据实战视频
下载链接: 链接:http://pan.baidu.com/s/1bToXK6 密码:7k43 解压密码: www.lthack.com 或者 2cifang.com 或者 2cifang.com_2 ...