ABP领域事件/事件总线
一、文件结构
文件名称 | 路径 | 描述 |
---|---|---|
IEventData.cs | \Abp\Events\Bus\ | 事件数据抽象接口 |
EventData.cs | \Abp\Events\Bus\ | 事件数据基本类实现 |
EventBusInstall.cs | \Abp\Events\Bus\ | 事件总线注册类 |
IEventBus.cs | \Abp\Events\Bus\ | 事件总线接口 |
EventBus.cs | \Abp\Events\Bus\ | 事件总线实现 |
IEventDataWithInheritableGenericArgument.cs | \Abp\Events\Bus\ | D6 |
NullEventBus.cs | \Abp\Events\Bus\ | D7 |
IEventHandler.cs | \Abp\Events\Bus\Handler | D7 |
IEventHandlerOfTEventData.cs | \Abp\Events\Bus\Handler | D8 |
ActionEventHandler.cs | \Abp\Events\Bus\Hanlder\Internals | D9 |
DomainEventEntry.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityChangedEventData.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityChangeEntry.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityChangeEventHelper.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityChangeReport.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityChangeType.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityChangingEventData.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityCreatedEventData.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityCreatingEventData.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityDeletedEventData.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityDeletingEventData.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityEventData.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityUpdatedEventData.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
EntityUpdatingEventData.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
IEntityChangeEventHelper.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
NullEntityChangeEventHelper.cs | \Abp\Events\Bus\Entities\ | 数据库实体相关事件. |
AbpHandledExceptionData.cs | \Abp\Events\Bus\Exception\ | D26 |
ExceptionData.cs | \Abp\Events\Bus\Exception\ | D27 |
IEventHandlerFactory.cs | \Abp\Events\Bus\Factories\ | D28 |
IocHandlerFactory.cs | \Abp\Events\Bus\Factories\ | D29 |
FactoryUnregistrar.cs | \Abp\Events\Bus\Factories\Internals\ | D30 |
SingleInstanceHandlerFactory.cs | \Abp\Events\Bus\Factories\Internals\ | 单例工厂 |
TransientEventHandlerFactory.cs | \Abp\Events\Bus\Factories\Internals\ | 瞬时工厂 |
二、大致流程
领域事件用于各个业务领域之间进行通信而又不相互依赖,是一种集中式的事件处理机制,各个模块之间都可以在任何地方订阅/发布事件。
在 EventBus 内部维护一个 Dictionary ,存放所有已经注册了的 EventData 类型的工厂,工厂负责生产处理器与销毁处理器,每当调用 Tirgger 方法的时候会去查询这个字典,并且调用相应的处理类方法。
1=>start: 注入 EventBus
2=>operation: 监听组件注册事件
3=>inputoutput: 注册 IEventHandler
4=>operation: Trigger 触发事件
5=>condition: 匹配处理器
6=>operation: 调用处理器
1->2->3->4->5
5(yes)->6
5(no)->4
三、具体解析
1.注册 EventBus 与 IEventHandler
事件总线通过 EventBusInstaller 来注册 EventBus 和监听事件。
public override void Initialize()
{
foreach (var replaceAction in ((AbpStartupConfiguration)Configuration).ServiceReplaceActions.Values)
{
replaceAction();
}
// 事件总线注册
IocManager.IocContainer.Install(new EventBusInstaller(IocManager));
IocManager.RegisterAssemblyByConvention(typeof(AbpKernelModule).GetAssembly(),
new ConventionalRegistrationConfig
{
InstallInstallers = false
});
}
而 EventBusInstaller 则需要注意的是这个方法:
private void Kernel_ComponentRegistered(string key, IHandler handler)
{
/* This code checks if registering component implements any IEventHandler<TEventData> interface, if yes,
* gets all event handler interfaces and registers type to Event Bus for each handling event.
*/
if (!typeof(IEventHandler).GetTypeInfo().IsAssignableFrom(handler.ComponentModel.Implementation))
{
return;
}
var interfaces = handler.ComponentModel.Implementation.GetTypeInfo().GetInterfaces();
foreach (var @interface in interfaces)
{
if (!typeof(IEventHandler).GetTypeInfo().IsAssignableFrom(@interface))
{
continue;
}
var genericArgs = @interface.GetGenericArguments();
if (genericArgs.Length == 1)
{
_eventBus.Register(genericArgs[0], new IocHandlerFactory(_iocResolver, handler.ComponentModel.Implementation));
}
}
}
在其内部针对每次 IocContainer 注册事件进行了监听,每当注册了一个类型之后,都会判断当前类型是否实现了 IEventHandler ,之后使用 GetInterfaces()
方法获取其具体实现的每一个接口,并分别获得其具体的 EventData 类型并在 EventBus 注册。
2.触发事件
开发人员可以在任意地方通过构造注入或者属性注入来获得 IEventBus 的实例,并使用其提供的 Trigger 方法来触发指定的事件。
在 EventBus 当中, Trigger 拥有4个重载方法, Trigger 还有一种异步实现是 TriggerAsync ,也拥有4个重载,原型分别如下:
void Trigger<TEventData>(TEventData eventData) where TEventData : IEventData;
void Trigger<TEventData>(object eventSource, TEventData eventData) where TEventData : IEventData;
void Trigger(Type eventType, IEventData eventData);
void Trigger(Type eventType, object eventSource, IEventData eventData);
Task TriggerAsync<TEventData>(TEventData eventData) where TEventData : IEventData;
Task TriggerAsync<TEventData>(object eventSource, TEventData eventData) where TEventData : IEventData;
Task TriggerAsync(Type eventType, IEventData eventData);
Task TriggerAsync(Type eventType, object eventSource, IEventData eventData);
核心是 public void Trigger(Type eventType, object eventSource, IEventData eventData)
方法,在其内部的具体调用使用了一层 try-catch 进行包裹。
public void Trigger(Type eventType, object eventSource, IEventData eventData)
{
var exceptions = new List<Exception>();
TriggerHandlingException(eventType, eventSource, eventData, exceptions);
if (exceptions.Any())
{
if (exceptions.Count == 1)
{
exceptions[0].ReThrow();
}
throw new AggregateException("More than one error has occurred while triggering the event: " + eventType, exceptions);
}
}
而在 TriggerHandlingException()
方法当中对已经注册好了的 Dictionary 进行遍历,获取工厂并生成处理方法进行调用,调用完成之后进行销毁。
private void TriggerHandlingException(Type eventType, object eventSource, IEventData eventData, List<Exception> exceptions)
{
//TODO: This method can be optimized by adding all possibilities to a dictionary.
eventData.EventSource = eventSource;
foreach (var handlerFactories in GetHandlerFactories(eventType))
{
foreach (var handlerFactory in handlerFactories.EventHandlerFactories)
{
var eventHandler = handlerFactory.GetHandler();
try
{
if (eventHandler == null)
{
throw new Exception($"Registered event handler for event type {handlerFactories.EventType.Name} does not implement IEventHandler<{handlerFactories.EventType.Name}> interface!");
}
var handlerType = typeof(IEventHandler<>).MakeGenericType(handlerFactories.EventType);
// 获取处理方法
var method = handlerType.GetMethod(
"HandleEvent",
new[] { handlerFactories.EventType }
);
method.Invoke(eventHandler, new object[] { eventData });
}
catch (TargetInvocationException ex)
{
exceptions.Add(ex.InnerException);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
finally
{
// 销毁对象
handlerFactory.ReleaseHandler(eventHandler);
}
}
}
四、使用方法
1.自动注册
首先需要定义你的事件数据实体,该实体用于触发事件的时候传递参数等操作。
数据实体必须实现 IEventData 接口或者继承自 EventData 。
public class TestEventData : EventData
{
public string Name { get; set; }
}
这里定义了一个 TestEventData 事件数据实体。
然后我们针对该事件数据实体编写处理程序。
public TestHandler : IEventHandler<TestEventData>,ITransientDependency
{
public void HandleEvent(TestEventData eventData)
{
Console.WriteLine(eventData.Name);
}
}
注意:在此处必须继承 ITransientDependency ,否则事件处理类是无法被注册的。
这样我们就针对 TestEventData 这种事件编写了一个处理处理程序,当程序任何地方调用 Trigger 的时候,会调用响应的事件处理方法。
在这里如果我们针对 TestEventData 注册两个处理器的话,在调用了 Trigger 之后,两个处理器都会被触发,但是先后顺序无法保证。
同时,一个处理器可以继承多个事件的处理实现,例如:
public class TestEventHandlerMulit : IEventHandler<TestEventDataChild>, IEventHandler<TestEventData>, ITransientDependency
{
public void HandleEvent(TestEventDataOther eventData)
{
Console.WriteLine(eventData.Name2);
}
public void HandleEvent(TestEventData eventData)
{
Console.WriteLine(eventData.Name);
}
}
这里针对 TestEventDataOther 和 TestEventData 都进行了注册,在被触发相应的事件之后,便会触发对应的事件处理程序。
2.手动注册
在具体代码实现当中也可以手动注册某些事件,手动注册无非就是将Abp在启动时进行注册的方法拿出来而已。直接使用 IEventBus 的 Register 方法即可进行注册。
在 IEventBus 接口当中,定义了 Register 方法用于手动注册事件,一种是传入一个响应委托:
EventBus.Register<TaskCompletedEventData>(eventData=>
{
Console.WriteLine($"TaskID={eventData.TaskId}");
});
还有一种方法是传入一个对象,且该对象实现了 IEventHandler 接口:
Eventbus.Register<TaskCompletedEventData>(new ActivityWriter());
该方法还有另外一个泛型重载,可以直接绑定事件与处理对象:
EventBus.Register<TaskCompletedEventData, ActivityWriter>();
3.取消注册
1.显示调用 Dispose()
//注册一个事件
Var registration = EventBus.Register<TaskCompletedEventData>(eventData => WriteActivity("A task is completed by id = " + eventData.TaskId));
//取消注册一个事件
registration.Dispose();
2.调用 UnRegister 方法
//创建一个处理器
var handler = new ActivityWriter();
//注册一个事件
EventBus.Register<TaskCompletedEventData>(handler);
//取消这个事件的注册
EventBus.Unregister<TaskCompletedEventData>(handler);