using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Collections; using Volo.Abp.EventBus.Distributed; using Volo.Abp.MultiTenancy; using Volo.Abp.Reflection; namespace Volo.Abp.EventBus { public abstract class EventBusBase : IEventBus { protected IServiceScopeFactory ServiceScopeFactory { get; } protected ICurrentTenant CurrentTenant { get; } protected IEventErrorHandler ErrorHandler { get; } protected EventBusBase( IServiceScopeFactory serviceScopeFactory, ICurrentTenant currentTenant, IEventErrorHandler errorHandler) { ServiceScopeFactory = serviceScopeFactory; CurrentTenant = currentTenant; ErrorHandler = errorHandler; } /// public virtual IDisposable Subscribe(Func action) where TEvent : class { return Subscribe(typeof(TEvent), new ActionEventHandler(action)); } /// public virtual IDisposable Subscribe() where TEvent : class where THandler : IEventHandler, new() { return Subscribe(typeof(TEvent), new TransientEventHandlerFactory()); } /// public virtual IDisposable Subscribe(Type eventType, IEventHandler handler) { return Subscribe(eventType, new SingleInstanceHandlerFactory(handler)); } /// public virtual IDisposable Subscribe(IEventHandlerFactory factory) where TEvent : class { return Subscribe(typeof(TEvent), factory); } public abstract IDisposable Subscribe(Type eventType, IEventHandlerFactory factory); public abstract void Unsubscribe(Func action) where TEvent : class; /// public virtual void Unsubscribe(ILocalEventHandler handler) where TEvent : class { Unsubscribe(typeof(TEvent), handler); } public abstract void Unsubscribe(Type eventType, IEventHandler handler); /// public virtual void Unsubscribe(IEventHandlerFactory factory) where TEvent : class { Unsubscribe(typeof(TEvent), factory); } public abstract void Unsubscribe(Type eventType, IEventHandlerFactory factory); /// public virtual void UnsubscribeAll() where TEvent : class { UnsubscribeAll(typeof(TEvent)); } /// public abstract void UnsubscribeAll(Type eventType); /// public virtual Task PublishAsync(TEvent eventData) where TEvent : class { return PublishAsync(typeof(TEvent), eventData); } /// public abstract Task PublishAsync(Type eventType, object eventData); public virtual async Task TriggerHandlersAsync(Type eventType, object eventData, Action onErrorAction = null) { var exceptions = new List(); await TriggerHandlersAsync(eventType, eventData, exceptions); if (exceptions.Any()) { var context = new EventExecutionErrorContext(exceptions, eventType, this); onErrorAction?.Invoke(context); await ErrorHandler.HandleAsync(context); } } protected virtual async Task TriggerHandlersAsync(Type eventType, object eventData , List exceptions) { await new SynchronizationContextRemover(); foreach (var handlerFactories in GetHandlerFactories(eventType)) { foreach (var handlerFactory in handlerFactories.EventHandlerFactories) { await TriggerHandlerAsync(handlerFactory, handlerFactories.EventType, eventData, exceptions); } } //Implements generic argument inheritance. See IEventDataWithInheritableGenericArgument if (eventType.GetTypeInfo().IsGenericType && eventType.GetGenericArguments().Length == 1 && typeof(IEventDataWithInheritableGenericArgument).IsAssignableFrom(eventType)) { var genericArg = eventType.GetGenericArguments()[0]; var baseArg = genericArg.GetTypeInfo().BaseType; if (baseArg != null) { var baseEventType = eventType.GetGenericTypeDefinition().MakeGenericType(baseArg); var constructorArgs = ((IEventDataWithInheritableGenericArgument)eventData).GetConstructorArgs(); var baseEventData = Activator.CreateInstance(baseEventType, constructorArgs); await PublishAsync(baseEventType, baseEventData); } } } protected virtual void SubscribeHandlers(ITypeList handlers) { foreach (var handler in handlers) { var interfaces = handler.GetInterfaces(); foreach (var @interface in interfaces) { if (!typeof(IEventHandler).GetTypeInfo().IsAssignableFrom(@interface)) { continue; } var genericArgs = @interface.GetGenericArguments(); if (genericArgs.Length == 1) { Subscribe(genericArgs[0], new IocEventHandlerFactory(ServiceScopeFactory, handler)); } } } } protected abstract IEnumerable GetHandlerFactories(Type eventType); protected virtual async Task TriggerHandlerAsync(IEventHandlerFactory asyncHandlerFactory, Type eventType, object eventData, List exceptions) { using (var eventHandlerWrapper = asyncHandlerFactory.GetHandler()) { try { var handlerType = eventHandlerWrapper.EventHandler.GetType(); using (CurrentTenant.Change(GetEventDataTenantId(eventData))) { if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(ILocalEventHandler<>))) { var method = typeof(ILocalEventHandler<>) .MakeGenericType(eventType) .GetMethod( nameof(ILocalEventHandler.HandleEventAsync), new[] { eventType } ); await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData })); } else if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IDistributedEventHandler<>))) { var method = typeof(IDistributedEventHandler<>) .MakeGenericType(eventType) .GetMethod( nameof(IDistributedEventHandler.HandleEventAsync), new[] { eventType } ); await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData })); } else { throw new AbpException("The object instance is not an event handler. Object type: " + handlerType.AssemblyQualifiedName); } } } catch (TargetInvocationException ex) { exceptions.Add(ex.InnerException); } catch (Exception ex) { exceptions.Add(ex); } } } protected virtual Guid? GetEventDataTenantId(object eventData) { return eventData switch { IMultiTenant multiTenantEventData => multiTenantEventData.TenantId, IEventDataMayHaveTenantId eventDataMayHaveTenantId when eventDataMayHaveTenantId.IsMultiTenant(out var tenantId) => tenantId, _ => CurrentTenant.Id }; } protected class EventTypeWithEventHandlerFactories { public Type EventType { get; } public List EventHandlerFactories { get; } public EventTypeWithEventHandlerFactories(Type eventType, List eventHandlerFactories) { EventType = eventType; EventHandlerFactories = eventHandlerFactories; } } // Reference from // https://blogs.msdn.microsoft.com/benwilli/2017/02/09/an-alternative-to-configureawaitfalse-everywhere/ protected struct SynchronizationContextRemover : INotifyCompletion { public bool IsCompleted { get { return SynchronizationContext.Current == null; } } public void OnCompleted(Action continuation) { var prevContext = SynchronizationContext.Current; try { SynchronizationContext.SetSynchronizationContext(null); continuation(); } finally { SynchronizationContext.SetSynchronizationContext(prevContext); } } public SynchronizationContextRemover GetAwaiter() { return this; } public void GetResult() { } } } }