事件總線功能庫,Reface.EventBus 詳細使用教程


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

相關鏈接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM