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
預定義事件
ABP定義了AbpHandleExceptionData,當自動處理任何異常時都會觸發這個事件。如果你想要獲得更多的關於異常的資訊(甚至ABP會自動記錄所有的異常),那麼這是特別有用的。註冊這個事件之後,異常發生時就會通知你。
對於實體的更改也有泛型的事件資料類:EntityCreatedEventData,EntityUpdateEventData和 EntityDeletedEventData。它們都定義在 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和它的兩個子類: TaskCompletedEventData和 TaskCreatedEventData:
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()方法會登出所有事件的所有控制代碼。
好文要頂