Reface.AppStarter 中的事件總線功能是通過 Reface.EventBus 提供的。
參考文章 : Reface.AppStarter 框架初探
使用 Reface.EventBus ,你可以在 Reface.AppStarter 框架外使用事件總線的功能。
Reface.EventBus 提供了兩個版本的包 Reface.EventBus 、 Reface.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 庫集成,比如 Autofac、Inject 等等。
在 .Net Core 版本中,已經實現了與 ServiceCollection 的集成,當你編寫 Web
項目時,你可以在 Startup.ConfigureService 中啟用 EventBus 的功能以及對程序集內的所有 監聽器 的發現與注冊。
以下是在 .Net Core 版本中,如何啟用 EventBus 以及對 監聽器 的發現與注冊
services // ServiceCollection 的實例
.AddEventBus() // 注冊運行 EventBus 的必要組件
.AddEventListeners(typeof(SomeClass).Assembly); // 掃描指定程序集內的 監聽器 並注冊到容器中
在 .Net Framework 中注冊監聽器
強烈建議將你的 IEventListenerFinder 與 IOC / 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 監聽新的事件
- 為 AListener 和 BListener 實現優先級接口,並讓 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) 時,都發生了什么事 ?
- 調用 IEventListenerFinder.CreateAllEventListeners() 獲取所有事件 處理器
- 對所有的 處理器 進行排序
- 沒有標記 IPrioritized 的處理器優先級為 int.MaxValue
- 排序按 Priority 從小到大排序
- 從 緩存中獲取 或 分析 哪些 處理器 將參與此次 事件 的處理
- 對參與此次 事件 的 處理器 從緩存或創建 DynamicMethod 用於執行 Handle 方法 , DynamicMethod 是通過 Emit 創建的,因此執行速度是非常快的
- 執行所有的 DynamicMethod
相關鏈接