受 CQRS 啟發,輕量級 EventBus 讓領域模型以 EventBus.Instance().Publish(new OrderAddedEvent()) 觸發事件,透過 XML 定義多對多訂閱關係,達成解耦。

圖 1 專案結構:Events、EventHandlers、EventBus 核心
<Events>
<Event>
<PublishEvent>ConsoleApplication2.code.Events.OrderAddedEvent</PublishEvent>
<SubscribedEvents>
<SubscribedEvent>ConsoleApplication2.code.EventHandlers.OrderAddedEventHandler_CachePrepare</SubscribedEvent>
<SubscribedEvent>ConsoleApplication2.code.EventHandlers.OrderAddedEventHandler_PDFGenerate</SubscribedEvent>
</SubscribedEvents>
</Event>
<Event>
<PublishEvent>ConsoleApplication2.code.Events.OrderDeletedEvent</PublishEvent>
<SubscribedEvents>
<SubscribedEvent>ConsoleApplication2.code.EventHandlers.OrderDeletedEventHandler_NotifyClient</SubscribedEvent>
<SubscribedEvent>ConsoleApplication2.code.EventHandlers.OrderDeletedEventHandler_RemoveCache</SubscribedEvent>
</SubscribedEvents>
</Event>
</Events>
public abstract class BaseEvent { }
public class OrderAddedEvent : BaseEvent { }
public class OrderDeletedEvent : BaseEvent {
public Guid OrderId { get; set; }
public OrderDeletedEvent(Guid orderId) { OrderId = orderId; }
}
public interface IEventHandler<T> where T : BaseEvent {
void Handle(T evt);
}
public class OrderDeletedEventHandler_NotifyClient : IEventHandler<OrderDeletedEvent> {
public void Handle(OrderDeletedEvent evt) {
Console.WriteLine("[Order deleted] Notified.");
}
}
private static Dictionary<Type, List<Type>> eventMapping =
new Dictionary<Type, List<Type>>();
XElement root = XElement.Load(Path.Combine(
AppDomain.CurrentDomain.BaseDirectory, "EventBus.xml"));
foreach (var evt in root.Elements("Event")) {
var handlers = new List<Type>();
Type publishType = Type.GetType(evt.Element("PublishEvent").Value);
foreach (var sub in evt.Element("SubscribedEvents").Elements("SubscribedEvent")) {
handlers.Add(Type.GetType(sub.Value));
}
eventMapping[publishType] = handlers;
}
public void Publish(BaseEvent evt) {
var matches = from kv in eventMapping
where kv.Key == evt.GetType()
select kv;
foreach (var pair in matches) {
foreach (Type handlerType in pair.Value) {
MethodInfo mi = handlerType.GetMethod("Handle");
if (mi == null) continue;
object handler = Activator.CreateInstance(handlerType);
mi.Invoke(handler, new object[] { evt });
}
}
}

圖 2 主控台輸出顯示兩個訂閱器依序處理事件
擴充建議:引入
IEventHandlerFactory支援 DI 容器管理 Handler 生命週期;加入非同步HandleAsync避免阻塞發布執行緒。
作者:玄魂
出處:https://www.cnblogs.com/xuanhun/