1. 程式人生 > >ABP理論學習之事件匯流排和領域事件

ABP理論學習之事件匯流排和領域事件

返回總目錄

本篇目錄

在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實現了。當你呼叫上面的建構函式時,實際上啥都沒做。

定義事件

觸發事件之前,應該先要定義該事件。事件是使用派生自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()方法會登出所有事件的所有控制代碼。

好文要頂