Reface.AppStarter 中的事件总线功能是通过 Reface.EventBus 提供的。

参考文章 : Reface.AppStarter 框架初探

使用 Reface.EventBus ,你可以在 Reface.AppStarter 框架外使用事件总线的功能。

Reface.EventBus 提供了两个版本的包 Reface.EventBusReface.Core.EventBus ,分别工作在 .Net Framework.Net Core 平台上。

除了一些细节的不同,这两个版本的使用方法完全相同。

基本篇

下面我们一起来了解如何使用 Reface.EventBus定义事件定义监听器注册监听器发布事件

定义事件

事件是一个单独类型,它继承于 Reface.EventBus.Event其子类 , 我们先看看 Event 的定义

    public class Event
{
public object Source { get; private set; } public Dictionary<string, object> Context { get; private set; } public Event(object source)
{
if (source == null)
throw new ArgumentNullException("source");
Source = source;
this.Context = new Dictionary<string, object>();
}
}

Event 要求一个 object 类型的构造函数,用来表示事件是由谁发布的,与 .Net 中的 event 模式中的 sender 含义相同。

属性 Source

从构造函数可以看出 , 可以通过属性 Source 获取到事件的发布者。

属性 Context

正如其名,是事件的上文对象,你可以向其中存取任何你希望的内容。该属性可以成为多个 监听者 之间 沟通 的桥梁。

使用该属性时需要注意 :

  • 监听者模式情况下无论控制执行顺序,因此要考虑到当前监听者处理时,某些上下文的内容尚未创建
  • 如果你的监听者对上下文有着严格的前后 顺序要求,请为监听器添加优先级的功能 (后文有介绍)

示例

我们定义一个事件,可以在控制台程序中,描述一个用户输入了一个命令的事件。

public class CommandReadEvent : Event
{
public string Command { get; private set; }
public CommandReadEvent(object source, string cmd) : base(source)
{
this.Command = cmd;
}
}

定义监听器

所有的监听器必须实现 IEventListener<TEvent> 接口,IEventHandler<T> 的定义如下

public interface IEventListener<TEvent>
where TEvent : Event
{
void Handle(TEvent @event);
}

泛型 TEvent

泛型指定了该监听器监听哪种事件类型的事件。

职责单一 是重要的设计思想。因此,一件监听器只能针对一个事件类型进行处理。

方法 Handler

该方法是事件处理的具体过程,参数就是事件类型。

同样,我们依照 职责单一 的原则,强烈建议一个监听器内只实现一个任务,多个任务由多个监听器实现。

监听器在哪儿 ?

EventBus 无法得知开发者把它们的 监听器 都写在哪儿,所以需要以某种方式发现它们,才能调度它们。

IEventListenerFinder 就是负责找到这些 监听器 并创建它们实例的组件。

关于 监听器 的创建

所有的 监听器 都是通过 IEventListenerFinder 创建的。

.Net Framework 版本中所有的 IEventListenerFinder 都不支持 IOC / DI , 如果你希望对 监听器 注入外部组件,可以考虑和成熟的 IOC / DI 库集成,比如 AutofacInject 等等。

.Net Core 版本中,已经实现了与 ServiceCollection 的集成,当你编写 Web

项目时,你可以在 Startup.ConfigureService 中启用 EventBus 的功能以及对程序集内的所有 监听器 的发现与注册。

以下是在 .Net Core 版本中,如何启用 EventBus 以及对 监听器 的发现与注册

services // ServiceCollection 的实例
.AddEventBus() // 注册运行 EventBus 的必要组件
.AddEventListeners(typeof(SomeClass).Assembly); // 扫描指定程序集内的 监听器 并注册到容器中
在 .Net Framework 中注册监听器

强烈建议将你的 IEventListenerFinderIOC / DI 容器集成。

但是,如果你没有这样的需求,你也可以通过以下两种方式向 EventBus 注册你的 监听器

1. 通过 config 文件注册监听器

首先,将 config* 文件添加 section 节点,建议使用 eventBus 作为 name 的值。

<section name="eventBus" type="Reface.EventBus.Configuration.EventBusSection, Reface.EventBus"/>

接下来在 configuration 节点内,添加 eventBus 节点

<configuration>
<eventBus>
<listeners>
<add type="Demo.Listeners.OnConsoleStarted02, Demo" />
<add type="Demo.Listeners.OnConsoleStarted01, Demo" />
<add type="更多的监听器类型名称" />
</listeners>
</eventBus>
</configuration>

类型名称应由 类型全名+逗号+程序集名 组成。

配置文件是 EventBus 默认的选项,你不需要做任何额外的编码,就可以发布事件了。

IEventBus eventBus = new EventBus();
eventBus.Publish(new MyEvent());

EventBus 发布事件时,会通过 Activator.CreateInstance(Type) 的方式创建这些 监听者,因此你的 监听者 的构造函数不可以有参数。

2. 扫描程序集以发现监听器

通过这种方式,你可以指定一些程序集,EventBus 会预先扫描它们,并将实现了 IEventListener 接口的类型记录。

config 模块相同,监听器 是通过 Activator.CreateInstance(Type) 的方式创建的,因此 监听者 的构造函数依然不可以有参数。

发布事件

IEventBus 是发布事件的组件,它只包含一个成员,Publish 用于发布事件。

public interface IEventBus
{
void Publish(Event @event);
}

我会在未来的版本中加入 AsyncPublish(Event) 方法,以用于异步发布事件。

库中只有一个 IEventBus 的实现类 : EventBus

该类是依赖 IEventListenerFinder 发现 监听器 并发布 事件

你也可以实现属于你自己的 IEventBus ,你可以让它通过 MQ 等其它方式来发布事件。

扩展篇

监听者的执行顺序

一个 Event 可以触发多个 监听器 的执行。在正常情况下,EventBus 只以 注册的顺序扫描程序集的顺序 来决定 监听器 的执行顺序。

在实际开发环境中,如果你对 监听器 的执行顺序有要求,比如,AListener 必须早于 BListener , 你可以选择以下两个解决方案

  • 设计一个新的事件,当 AListener 完成后触发,BListener 监听新的事件
  • AListenerBListener 实现优先级接口,并让 AListener 优于 BListener

库内有接口 IPrioritized , 它可以让你的 监听器 额外提供一个 优先级 的属性,这是一个 int 型的属性,数值越小,就越先执行。

对于未实现 IPrioritized监听器,优先级一律是 int.MaxValue

public class AListener : IEventListener<SomeEvent>, IPrioritized
{
public int Priority => 0; public void Handle(SomeEvent @event)
{
Console.WriteLine("Console Started 01");
}
}

Event 的继承

使用 Reface.EventBus ,你可以设计具有继承关系的 Event 类型。

比如

// 删除数据的事件
public class DataDeletingEvent : Event {} // 删除用户的事件
public class UserDeletingEvent : DataDeletingEvent {} // 删除部门的事件
public class DeptDeletingEvent : DataDeletingEvent {}

当你对事件监听时,你可以编写一个 监听器 来监听 DataDeletingEvent

public class DataDeletingEventListener : IEventListener<DataDeletingEvent>
{ }

该处理器可以处理所有 DataDeletingEvent其子类 的事件。

运行机制

Publish(TEvent) 时,都发生了什么事 ?

  1. 调用 IEventListenerFinder.CreateAllEventListeners() 获取所有事件 处理器
  2. 对所有的 处理器 进行排序
  • 没有标记 IPrioritized 的处理器优先级为 int.MaxValue
  • 排序按 Priority 从小到大排序
  1. 缓存中获取分析 哪些 处理器 将参与此次 事件 的处理
  2. 对参与此次 事件处理器 从缓存或创建 DynamicMethod 用于执行 Handle 方法 , DynamicMethod 是通过 Emit 创建的,因此执行速度是非常快的
  3. 执行所有的 DynamicMethod

相关链接

事件总线功能库,Reface.EventBus 详细使用教程的更多相关文章

  1. Android事件总线分发库EventBus3.0的简单讲解与实践

    Android事件总线分发库EventBus的简单讲解与实践 导语,EventBus大家应该不陌生,EventBus是一款针对Android优化的发布/订阅事件总线.主要功能是替代Intent,Han ...

  2. 45、Android事件总线分发库的使用

    事件总线分发库EventBus和Otto的简介及对比 什么是事件总线管理: a.将事件放到队列里,用于管理和分发b.保证应用的各个部分之间高效的通信及数据.事件分发c.模块间解耦 Event Bus是 ...

  3. 基于ASP.NET Core 5.0使用RabbitMQ消息队列实现事件总线(EventBus)

    文章阅读请前先参考看一下 https://www.cnblogs.com/hudean/p/13858285.html 安装RabbitMQ消息队列软件与了解C#中如何使用RabbitMQ 和 htt ...

  4. C# 事件总线 EventBus

    1. 引言 事件总线这个概念对你来说可能很陌生,但提到观察者(发布-订阅)模式,你也许就很熟悉.事件总线是对发布-订阅模式的一种实现.它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需 ...

  5. SOFA 源码分析— 事件总线

    前言 大部分框架都是事件订阅功能,即观察者模式,或者叫事件机制.通过订阅某个事件,当触发事件时,回调某个方法.该功能非常的好用,而 SOFA 内部也设计了这个功能,并且内部大量使用了该功能.来看看是如 ...

  6. .Net 事件总线之Autofac解耦

    事件总线是通过一个中间服务,剥离了常规事件的发布与订阅(消费)强依赖关系的一种技术实现.事件总线的基础知识可参考圣杰的博客[事件总线知多少] 本片博客不再详细概述事件总线基础知识,核心点放置使用Aut ...

  7. Autofac解耦事件总线

    事件总线之Autofac解耦 事件总线是通过一个中间服务,剥离了常规事件的发布与订阅(消费)强依赖关系的一种技术实现.事件总线的基础知识可参考圣杰的博客[事件总线知多少] 本片博客不再详细概述事件总线 ...

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

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

  9. 事件总线(Event Bus)知多少

    源码路径:Github-EventBus 简书同步链接 1. 引言 事件总线这个概念对你来说可能很陌生,但提到观察者(发布-订阅)模式,你也许就很熟悉.事件总线是对发布-订阅模式的一种实现.它是一种集 ...

随机推荐

  1. Mybatis中的# 与 $

    我们说MyBatis有两种注入参数的方式, - 一种是#{} - 另一种是${} 这两种从使用功能来看差距不大,那为什么会强推使用#? ${}使用的是拼接字符串,#{}使用的是占位符的方法,经过了处理 ...

  2. JUnit 3.8.1 源码学习简记

    先记录一个整理的流程 1.首先使用TestSuite获取一个TestCase中的所有测试方法(方法名以test开头的方法),为每个方法生成一个TestCase实例并保存,实例中有个字段保存对应的方法名 ...

  3. 如何将icon图标库引入自己的项目中

    ---恢复内容开始--- 今天是18年的国庆,趁着国庆的这股开心劲儿,开开心心的写点东西: 第一篇:关于如何将icon图标库引入自己的项目(此方法Taro,微信小程序,支付宝小程序等均适用,不会存在不 ...

  4. 搭建DVWA Web渗透测试靶场

    文章更新于:2020-04-13 按照惯例,需要的文件附上链接放在文首. 文件名:DVWA-1.9-2020.zip 文件大小:1.3 M 文件说明:这个是新版 v1.9 (其实是 v1.10开发版) ...

  5. 汇编刷题 已知整数变量A和B,试编写完成下列操作的程序

    1.若两个数中有一个是奇数,一个是偶数,则将它们互换储存地址 2.若两个数都是奇数,则分别加一 3.若两个数都是偶数,则不变 DATA SEGMENT A DB 12H B DB 25H DATA E ...

  6. 在MAC上如何使用SQL Server

    由于小编在这学期要学习数据库原理这门课程,需要用到SQL Server,然而大家都知道SQL Server目前是只能在Windows上使用,我们在mac电脑上如何使用呢?我们可以借助目前比较火的Doc ...

  7. vue项目中使用bpmn-流程图预览篇

    前情提要 上文已经实现了节点操作的前进.后退.导入.导出等操作,今日来实现“流程图预览”,以及视图的放大缩小 前提:项目安装过bpmn,安装可见上篇文章 实现要点 bpmn提供了两个神器:Modele ...

  8. k8s~helm镜像版本永远不要用latest

    对于容器编排工具k8s来说,你可以使用它规定的yaml格式的脚本,使用客户端kubectl来与k8s进行通讯,将你定义好的yaml部署脚本应用到k8s集群上,而这对yaml脚本一般来说都是很像的,就是 ...

  9. CentOS7.5 使用Docker部署Jumpserver

    1.环境准备 # 查看系统版本 $ cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) # 查看内核版本 $ uname -a L ...

  10. Go语言讲解深拷贝与浅拷贝

    我们在开发中会经常的把一个变量复制给另一个变量,那么这个过程,可能是深浅拷贝,那么今天帮大家区分一下这两个拷贝的区别和具体的区别. 一.概念 1.深拷贝(Deep Copy): 拷贝的是数据本身,创造 ...