可以看到,高亮的这一行,通过Autofac创建了一个新的LifetimeScope,在这个Scope中,通过eventName来获得一个subscription对象(也就是EventHandler的注册信息),进而通过scope的ResolveOptional调用来获得新的EventHandler实例。基本过程就是这样,目前也不需要纠结IDynamicIntegrationEventHandler是干什么用的,也不需要纠结为什么要使用dynamic来保存事件数据。重点是,autofac的BeginLifetimeScope方法调用创建了一个新的IoC Scope,在这个Scope中解析(resolve)了新的EventHandler实例。在eShopOnContainer案例中,EventBusRabbitMQ的设计是特定的,必须依赖于Autofac作为依赖注入框架。或许这部分设计可以进一步改善,使得EventBusRabbitMQ不会强依赖于Autofac。
接下来,我们会引入一个新的概念:事件处理器执行上下文,使用类似的方式来解决对象生命周期问题。
事件处理器执行上下文事件处理器执行上下文(Event Handler Execution Context, EHEC)为事件处理器提供了一个完整的生命周期管理机制,在这套机制中,事件处理器及其引用的对象资源可以被正常创建和正常销毁。现在让我们一起看看,如何在EdaSample的案例代码中使用事件处理器执行上下文。
事件处理器执行上下文的接口定义如下,当然,这部分接口是放在EdaSample.Common.Events目录下,作为消息系统的框架代码提供给调用方:
public interface IEventHandlerExecutionContext { void RegisterHandler<TEvent, THandler>() where TEvent : IEvent where THandler : IEventHandler<TEvent>; void RegisterHandler(Type eventType, Type handlerType); bool HandlerRegistered<TEvent, THandler>() where TEvent : IEvent where THandler : IEventHandler<TEvent>; bool HandlerRegistered(Type eventType, Type handlerType); Task HandleEventAsync(IEvent @event, CancellationToken cancellationToken = default); }这个接口主要包含三种方法:注册事件处理器、判断事件处理器是否已经注册,以及对接收到的事件消息进行处理。整个结构还是非常清晰简单的。现在需要实现这个接口。根据上面的分析,这个接口的实现是需要依赖于IoC容器的,目前简单起见,我们仅使用微软ASP.NET Core标准的Dependency Injection框架来实现,当然,也可以使用Autofac,取决于你怎样去实现上面这个接口。需要注意的是,由于该接口的实现是需要依赖于第三方组件的(在这里是微软的Dependency Injection框架),因此,最佳做法是新建一个类库,并引用EdaSample.Common程序集,并在这个新的类库中,依赖Dependency Injection框架来实现这个接口。
以下是基于Microsoft.Extensions.DependencyInjection框架来实现的事件处理器执行上下文完整代码,这里有个兼容性问题,就是构造函数的第二个参数:serviceProviderFactory。在Microsoft.Extensions.DependencyInjection框架2.0版本之前,IServiceCollection.BuildServiceProvider方法的返回类型是IServiceProvider,但从2.0开始,它的返回类型已经从IServiceProvider接口,变成了ServiceProvider类。这里引出了框架设计的另一个原则,就是依赖较低版本的.NET Core,以便获得更好的兼容性。如果我们的EdaSample是使用.NET Core 1.1开发的,那么当下面这个类被直接用在ASP.NET Core 2.0的项目中时,如果不通过构造函数参数传入ServiceProvider创建委托,而是直接在代码中使用registry.BuildServiceProvider调用,就会出现异常。
public class EventHandlerExecutionContext : IEventHandlerExecutionContext { private readonly IServiceCollection registry; private readonly Func<IServiceCollection, IServiceProvider> serviceProviderFactory; private readonly ConcurrentDictionary<Type, List<Type>> registrations = new ConcurrentDictionary<Type, List<Type>>(); public EventHandlerExecutionContext(IServiceCollection registry, Func<IServiceCollection, IServiceProvider> serviceProviderFactory = null) { this.registry = registry; this.serviceProviderFactory = serviceProviderFactory ?? (sc => registry.BuildServiceProvider()); } public async Task HandleEventAsync(IEvent @event, CancellationToken cancellationToken = default(CancellationToken)) { var eventType = @event.GetType(); if (this.registrations.TryGetValue(eventType, out List<Type> handlerTypes) && handlerTypes?.Count > 0) { var serviceProvider = this.serviceProviderFactory(this.registry); using (var childScope = serviceProvider.CreateScope()) { foreach(var handlerType in handlerTypes) { var handler = (IEventHandler)childScope.ServiceProvider.GetService(handlerType); if (handler.CanHandle(@event)) { await handler.HandleAsync(@event, cancellationToken); } } } } } public bool HandlerRegistered<TEvent, THandler>() where TEvent : IEvent where THandler : IEventHandler<TEvent> => this.HandlerRegistered(typeof(TEvent), typeof(THandler)); public bool HandlerRegistered(Type eventType, Type handlerType) { if (this.registrations.TryGetValue(eventType, out List<Type> handlerTypeList)) { return handlerTypeList != null && handlerTypeList.Contains(handlerType); } return false; } public void RegisterHandler<TEvent, THandler>() where TEvent : IEvent where THandler : IEventHandler<TEvent> => this.RegisterHandler(typeof(TEvent), typeof(THandler)); public void RegisterHandler(Type eventType, Type handlerType) { Utils.ConcurrentDictionarySafeRegister(eventType, handlerType, this.registrations); this.registry.AddTransient(handlerType); } }