返回总目录


本篇目录

在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,EntityUpdateEventDataEntityDeletedEventData。它们都定义在 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和它的两个子类: TaskCompletedEventDataTaskCreatedEventData:

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理论学习之事件总线和领域事件的更多相关文章

  1. ABP的事件总线和领域事件(EventBus & Domain Events)

    http://www.aspnetboilerplate.com/Pages/Documents/EventBus-Domain-Events EventBus EventBus是个单例,获得Even ...

  2. DDD理论学习系列(9)-- 领域事件

    DDD理论学习系列--案例及目录 1. 引言 A domain event is a full-fledged part of the domain model, a representation o ...

  3. [Abp vNext 源码分析] - 13. 本地事件总线与分布式事件总线 (Rabbit MQ)

    一.简要介绍 ABP vNext 封装了两种事件总线结构,第一种是 ABP vNext 自己实现的本地事件总线,这种事件总线无法跨项目发布和订阅.第二种则是分布式事件总线,ABP vNext 自己封装 ...

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

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

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

    ABP入门系列目录--学习Abp框架之实操演练 源码路径:Github-LearningMpaAbp 1.引言 最近刚学习了下DDD中领域事件的理论知识,总的来说领域事件主要有两个作用,一是解耦,二是 ...

  6. ABP框架 - 领域事件(EventBus)

    文档目录 本节内容: EventBus 注入 IEventBus 获取默认实例 定义事件 预定义事件 处理完异常 实体修改 触发事件 处理事件 处理基类事件 处理程序异常 处理多个事件 处理程序注册 ...

  7. [Abp 源码分析]九、事件总线

    0.简介 事件总线就是订阅/发布模式的一种实现,本质上事件总线的存在是为了降低耦合而存在的. 从上图可以看到事件由发布者发布到事件总线处理器当中,然后经由事件总线处理器调用订阅者的处理方法,而发布者和 ...

  8. 浅入 ABP 系列(4):事件总线

    浅入 ABP 系列(4):事件总线 版权护体作者:痴者工良,微信公众号转载文章需要 <NCC开源社区>同意. 目录 浅入 ABP 系列(4):事件总线 事件总线 关于事件总线 为什么需要这 ...

  9. Android事件总线

    Android中Activity.Service.Fragment之间的相互通信比较麻烦,主要有以下一些方法: (1)使用广播,发送者发出广播,接收者接收广播后进行处理: (2)使用Handler和M ...

随机推荐

  1. ****LINUX命令(含GIT命令)个人总结

    参考文章: 每日一个linux命令 http://www.cnblogs.com/peida/tag/%E6%AF%8F%E6%97%A5%E4%B8%80linux%E5%91%BD%E4%BB%A ...

  2. CSS系列:在HTML中引入CSS的方法

    HTML与CSS是两个作用不同的语言,它们同时对一个网页产生作用,因此必须将CSS与HTML链接在一起使用.在HTML中,引入CSS的方法主要有4种:行内式.内嵌式.导入式和链接式. 1. 行内式 行 ...

  3. CPT单臂路由

    路由器配置:Router>enableRouter#configure terminal        进入全局配置模式Router(config)#int fa0/1        进入端口R ...

  4. C语言语法 typedef小结

    在总结typedef之前,先了解一个专业术语: 常量指针(const pointer):常量指针在定义的时候必须被初始化,而且一旦初始化完成,则它的值就不能再改变. int errNumb = 0; ...

  5. Mac下golang开发环境配置

    go语言在开发效率和运行效率中的优势让很多人青睐,所以有倾向打算转向go语言的开发. 下面介绍在Mac OS X中golang的开发环境配置. 1.安装brew brew是一个mac下的由ruby开发 ...

  6. PTAM 编译安装 on MAC(mavericks)

    最近有需要研究PTAM,所以需要在我的MAC上编译安装,整个过程让人非常崩溃各种问题陷阱.现在整理一下编译过程.我已经成功在MAC上编译.那么接下来我会详细讲解整个过程: 注意: 部分链接可能需要FQ ...

  7. url中的特殊字符问题

    在 使用url进行参数传递时,经常会传递一些中文名(或含有特殊字符)的参数或URL地址,在后台处理时会发生转换错误.在有些传递页面使用GB2312, 而在接收页面使用UTF8,这样接收到的参数就可能会 ...

  8. appzapper注册码

    Appzapper for mac是MAC OS上的一款软件,可以非常方便的卸载自己不喜欢的软件,非常的快速便捷,卸载的时候不会有残留. 下载地址:http://www.pc6.com/mac/114 ...

  9. Time-travel Models

    1. Standard Iterative Branching model   Source Code   Butterfly Effect   Next   Edge of Tomorrow   D ...

  10. leetcode 199 :Binary Tree Right Side View

    // 我的代码 package Leetcode; /** * 199. Binary Tree Right Side View * address: https://leetcode.com/pro ...