本篇目錄
在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實現了null對象模式。當你調用上面的構造函數時,實際上啥都沒做。
定義事件###
觸發事件之前,應該先要定義該事件。事件是使用派生自EventData的類來表示的。假設我們想當一個任務task完成時觸發一個事件:
public class TaskCompletedEventData : EventData
{
public int TaskId { get; set; }
}
該類包含了類處理事件需要的屬性。EventData類定義了 EventSource(事件源)和 EventTime(事件觸發時間)屬性。
預定義事件
ABP定義了AbpHandleExceptionData,當自動處理任何異常時都會觸發這個事件。如果你想要獲得更多的關於異常的信息(甚至ABP會自動記錄所有的異常),那么這是特別有用的。注冊這個事件之后,異常發生時就會通知你。
對於實體的更改也有泛型的事件數據類:EntityCreatedEventData
觸發事件###
觸發一個事件很簡單,如下所示:
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
