ABP 领域事件

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
 2
 3
 4
 5
 6
 7
 8
 9
10
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 和监听事件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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 则需要注意的是这个方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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个重载,原型分别如下:

1
2
3
4
5
6
7
8
9
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 进行包裹。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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 进行遍历,获取工厂并生成处理方法进行调用,调用完成之后进行销毁。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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 。

1
2
3
4
public class TestEventData : EventData
{
    public string Name { get; set; }
}

这里定义了一个 TestEventData 事件数据实体。 然后我们针对该事件数据实体编写处理程序。

1
2
3
4
5
6
7
public TestHandler : IEventHandler<TestEventData>,ITransientDependency
{
    public void HandleEvent(TestEventData eventData)
    {
        Console.WriteLine(eventData.Name);
    }
}

注意:在此处必须继承 ITransientDependency ,否则事件处理类是无法被注册的。

这样我们就针对 TestEventData 这种事件编写了一个处理处理程序,当程序任何地方调用 Trigger 的时候,会调用响应的事件处理方法。 在这里如果我们针对 TestEventData 注册两个处理器的话,在调用了 Trigger 之后,两个处理器都会被触发,但是先后顺序无法保证。

同时,一个处理器可以继承多个事件的处理实现,例如:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
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 方法用于手动注册事件,一种是传入一个响应委托:

1
2
3
4
EventBus.Register<TaskCompletedEventData>(eventData=>
{
    Console.WriteLine($"TaskID={eventData.TaskId}");
});

还有一种方法是传入一个对象,且该对象实现了 IEventHandler 接口:

1
Eventbus.Register<TaskCompletedEventData>(new ActivityWriter());

该方法还有另外一个泛型重载,可以直接绑定事件与处理对象:

1
EventBus.Register<TaskCompletedEventData, ActivityWriter>();

3.取消注册

1.显示调用 Dispose()

1
2
3
4
//注册一个事件
Var registration = EventBus.Register<TaskCompletedEventData>(eventData => WriteActivity("A task is completed by id = " + eventData.TaskId));
//取消注册一个事件
registration.Dispose();

2.调用 UnRegister 方法

1
2
3
4
5
6
//创建一个处理器
var handler = new ActivityWriter();
//注册一个事件
EventBus.Register<TaskCompletedEventData>(handler);
//取消这个事件的注册
EventBus.Unregister<TaskCompletedEventData>(handler);
Built with Hugo
主题 StackJimmy 设计