ABP领域层——领域事件(Domain events)
ABP领域层——领域事件(Domain events)
基于DDD的现代ASP.NET开发框架--ABP系列之14、ABP领域层——领域事件(Domain events)
ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。
ABP的官方网站:http://www.aspnetboilerplate.com
ABP在Github上的开源项目:https://github.com/aspnetboilerplate
在C#中,一个类可以定义其专属的事件并且其它类可以注册该事件并监听,当事件被触发时可以获得事件通知。这对于对于桌面应用程序或独立的Windows Service来说非常有用。但是, 对于Web应用程序来说会有点问题,因为对象是根据请求(request)被创建并且它们的生命周期都很短暂。我们很难注册其它类别的事件。同样地,直接注册其它类别的事件也造成了类之间的耦合性。
在应用系统中,领域事件被用于解耦并且重用(re-use)商业逻辑。
事件总线
事件总线为一个单体(singleton)的对象,它由所有其它类所共享,可通过它触发和处理事件。要使用这个事件总线,你需要引用它。你可以用两种方式来实现:
获取默认实例( Getting the default instance)
你可以直接使用EventBus.Default。它是全局事件总线并且可以如下方式使用:
EventBus.Default.Trigger(...); //触发事件
注入IEventBus事件接口(Injecting IEventBus)
除了直接使用EventBus.Default外,你还可以使用依赖注入(DI)的方式来取得IEventBus的参考。这利于进行单元测试。在这里,我们使用属性注入的范式:
public class TaskAppService : ApplicaService {
public IEventBus EventBus { get; set; }
public TaskAppService() {
EventBus = NullEventBus.Instance;
}
}
注入事件总线,采用属性注入比建构子注入更适合。事件是由类所描述并且该事件对象继承自EventData。假设我们想要触发某个事件于某个任务完成后:
public class TaskCompletedEventData : EventData {
public int TaskId { get; set; }
}
这个类所包含的属性都是类在处理事件时所需要的。EventData类定义了EventSource(那个对象触发了这个事件)和EventTime(何时触发)属性。
定义事件
ABP定义AbpHandledExceptionData事件并且在异常发生的时候自动地触发这个事件。这在你想要取得更多关于异常的信息时特别有用(即便ABP已自动地纪录所有的异常)。你可以注册这个事件并且设定它的触发时机是在异常发生的时候。
ABP也提供在实体变更方面许多的通用事件数据类: EntityCreatedEventData, EntityUpdatedEventData和EntityDeletedEventData。它们被定义在Abp.Events.Bus.Entitis命名空间中。当某个实体新增/更新/删除后,这些事件会由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: 已完成数据库上的任务
EventBus.Trigger(new TaskCompletedEventData { TaskId = 42 } );
}
}
这里有一些触发方法的重载:
EventBus.Trigger<TaskcompletedEventData>(new TaskCompletedEventData { TaskId = 42});
EventBus.Trigger(this, new TaskCompletedEventData { TaskId = 42 });
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已集成到依赖注入系统中。就如同我们在上例中实现ITransientDependency那样,当TaskCompleted事件触发,它会创建一个新的ActivityWriter类的实体并且调用它的HandleEvent方法,并接着释放它。详情请见依赖注入(DI)一文。
基础事件的处理(Handling base events)
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来处理这两个事件:
public class ActivityWriter : IEventHandler<TaskEventData>, ITransientDependency {
public void HandleEvent(TaskEventData eventData) {
if(eventData is TaskCreatedEventData) {
...
}else{
...
}
}
}
当然,你也可以实现IEventHandler来处理所有的事件,如果你真的想要这样做的话(译者注:作者不太建议这种方式)。
处理多个事件(Handling multiple events)
在单个处理器(handler)中我们可以可以处理多个事件。此时,你应该针对不同事件实现IEventHandler。范例如下:
public class ActivityWriter :
IEventHandler<TaskCompletedEventData>,
IEventHandler<TaskCreatedEventData>,
ITransientDependency
{
public void HandleEvent(TaskCompletedEventData eventData) {
//TODO: 处理事件
}
public void HandleEvent(TaskCreatedEventData eventData) {
//TODO: 处理事件
}
}
注册处理器
我们必需注册处理器(handler)到事件总线中来处理事件。
自动型Automatically
ABP扫描所有实现IEventHandler接口的类,并且自动注册它们到事件总线中。当事件发生, 它通过依赖注入(DI)来取得处理器(handler)的引用对象并且在事件处理完毕之后将其释放。这是比较建议的事件总线使用方式于ABP中。
手动型(Manually)
也可以通过手动注册事件的方式,但是会有些问题。在Web应用程序中,事件的注册应该要在应用程序启动的时候。当一个Web请求(request)抵达时进行事件的注册,并且反复这个行为。这可能会导致你的应用程序发生一些问题,因为注册的类可以被调用多次。同样需要注意的是,手动注册无法与依赖注入系统一起使用。
ABP提供了多个事件总线注册方法的重载(overload)。最简单的一个重载方法是等待委派(delegate)或Lambda。
EventBus.Register<TaskCompletedEventData>(eventData =>
{
WriteActivity("A task is completed by id = " + eventData.TaskId);
});
因此,事件:task completed会发生,而这个Lambda方法会被调用。第二个重载方法等待的是一个对象,该对象实现了IEventHandler:
Eventbus.Register<TaskCompletedEventData>(new ActivityWriter());
相同的例子,如果ActivityWriter因事件而被调用。这个方法也有一个非泛型的重载。另一个重载接受两个泛化的参数:
EventBus.Register<TaskCompletedEventData, ActivityWriter>();
此时,事件总线创建一个新的ActivityWriter于每个事件。当它释放的时候,它会调用ActivityWriter.Dispose方法。
最后,你可以注册一个事件处理器工厂(event handler factory)来负责创建处理器。处理器工厂有两个方法: GetHandler和ReleaseHandler,范例如下:
public class ActivityWriterFactory : IEventHandlerFactory {
public IEventHandler GetHandler() {
return new ActivityWriter();
}
public void ReleaseHandler(IEventHandler handler) {
//TODO: 释放ActivityWriter实体(处理器)
}
}
ABP也提供了特殊的工厂类,IocHandlerFactory,通过依赖注入系统,IocHandlerFactory可以用来创建或者释放(dispose)处理器。ABP可以自动化注册IocHandlerFactory。因此,如果你想要使用依赖注入系统,请直接使用自动化注册的方式。
取消注册事件
当你手动注册事件总线,你或许想要在之后取消注册。最简单的取消事件注册的方式即为registration.Dispose()。举例如下:
//注册一个事件
Var registration = EventBus.Register<TaskCompletedEventData>(eventData => WriteActivity("A task is completed by id = " + eventData.TaskId));
//取消注册一个事件
registration.Dispose();
当然,取消注册可以在任何地方任何时候进行。保存(keep)好注册的对象并且在你想要取消注册的时候释放(dispose)掉它。所有注册方法的重载(overload)都会返回一个可释放(disposable)的对象来取消事件的注册。
事件总线也提供取消注册方法。使用范例:
//创建一个处理器
var handler = new ActivityWriter();
//注册一个事件
EventBus.Register<TaskCompletedEventData>(handler);
//取消这个事件的注册
EventBus.Unregister<TaskCompletedEventData>(handler);
它也提供重载的方法给取消注册的委派和工厂。取消注册处理器对象必须与之前注册的对象是同一个。
最后,EventBus提供一个UnregisterAll()方法来取消某个事件所有处理器的注册,而UnregisterAll()方法则是所有事件的所有处理器。
希望更多国内的架构师能关注到ABP这个项目,也许这其中有能帮助到您的地方,也许有您的参与,这个项目可以发展得更好。
欢迎加ABP架构设计交流QQ群:134710707
ABP领域层——领域事件(Domain events)的更多相关文章
- ABP(现代ASP.NET样板开发框架)系列之14、ABP领域层——领域事件(Domain events)
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之14.ABP领域层——领域事件(Domain events) ABP是“ASP.NET Boilerplate P ...
- ABP领域层知识回顾之---实体
标题:重温ABP领域层 1. 前言 最近一段时间一直在看<ABP的开发指南>(基于DDD的经典分层架构思想).因为之前一段时间刚看完<领域驱动设计:软件核心复杂性应对之道>, ...
- ABP(现代ASP.NET样板开发框架)系列之10、ABP领域层——实体
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之10.ABP领域层——实体 ABP是“ASP.NET Boilerplate Project (ASP.NET样板 ...
- ABP入门系列(3)——领域层创建实体
这一节我们主要和领域层打交道.首先我们要对ABP的体系结构以及从模板创建的解决方案进行一一对应.网上有代码生成器去简化我们这一步的任务,但是不建议初学者去使用. 一.首先来看看ABP体系结构 领域层就 ...
- ABP领域层——实体
ABP领域层——实体 基于DDD的现代ASP.NET开发框架--ABP系列之10.ABP领域层——实体 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的 ...
- ABP入门系列(2)——领域层创建实体
ABP入门系列目录--学习Abp框架之实操演练 这一节我们主要和领域层打交道.首先我们要对ABP的体系结构以及从模板创建的解决方案进行一一对应.网上有代码生成器去简化我们这一步的任务,但是不建议初学者 ...
- [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)
一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...
- ABP领域层创建实体
原文作者:圣杰 原文地址:ABP入门系列(2)——领域层创建实体 在原文作者上进行改正,适配ABP新版本.内容相同 这一节我们主要和领域层打交道.首先我们要对ABP的体系结构以及从模板创建的解决方案进 ...
- ABP领域层
1.实体Entites 1.1 概念 实体是DDD(领域驱动设计)的核心概念之一. 实体是具有唯一标识的ID且存储在数据库总.实体通常被映射成数据库中的一个表. 在ABP中,实体继承自Entity类. ...
随机推荐
- jquey :eq(1)
$("#div_Goods .datagrid-row .numberbox:eq(1)") $("#div_Goods .datagrid-row .numberbox ...
- add-apt-repository
也许你早已注意到,我们在介绍软件的时候,在安装这块一般都提供 PPA 源安装方式,这是一种快速方便的软件安装方法,但对于一些新手来说,对 PPA 并不是很熟悉,下面我们就详细介绍一下. # 我们先了解 ...
- 设置Windows 8.1屏幕自己主动旋转代码, Auto-rotate function code
程序代码实现启用或禁用Windows 8.1 Tablet的自己主动旋转功能 方法一:使用SetDisplayAutoRotationPreferences函数功能 #include <Wind ...
- 学派Delphi方法(推荐)——————————【Badboy】
Delphi是一个新的可视化编程环境, 提供了一种方便.快捷的Windows使用顺序开发工具. 它使用了MicrosoftWindows图形用户界面的很多先进特性和设计思想. 本文就给读者引见学Del ...
- putty中的一些经常使用操作
(和Linux中操作差点儿相同s) 删除文件夹 rm -rf /home/apache-tomcat-8.0.9 就会把home下的apache-tomcat-8.0.9目录给删除了 删除文件 rm ...
- POJ 2914 Minimum Cut 最小割图论
Description Given an undirected graph, in which two vertices can be connected by multiple edges, wha ...
- cocos2d之Box2D详细说明 鼠标联合实现
cocos2d之Box2D具体解释 鼠标关节实现 DionysosLai2014-5-7 我们常常要移动物理世界中的某个物体,例如说石头.木块等.假设我们直接改变这些物体的位置,让这些物体尾随我们手指 ...
- 匿名方法,Lambda表达式,高阶函数
原文:匿名方法,Lambda表达式,高阶函数 匿名方法 c#2.0引入匿名方法,不必创建单独的方法,因此减少了所需的编码系统开销. 常用于将委托和匿名方法关联,例如1. 使用委托和方法关联: this ...
- hdu1964之插头DP求最优值
Pipes Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Subm ...
- 【rman,1】经典案例增量备份
一.备份策略: 1.星期天晚上 -level 0 backup performed(全备份) 2.星期一晚上 -level 2 backup performed 3.星期二晚上 ...