Open Source Web Application Framework for ASP.NET Core
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

269 lines
10 KiB

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;
}
/// <inheritdoc/>
public virtual IDisposable Subscribe<TEvent>(Func<TEvent, Task> action) where TEvent : class
{
return Subscribe(typeof(TEvent), new ActionEventHandler<TEvent>(action));
}
/// <inheritdoc/>
public virtual IDisposable Subscribe<TEvent, THandler>()
where TEvent : class
where THandler : IEventHandler, new()
{
return Subscribe(typeof(TEvent), new TransientEventHandlerFactory<THandler>());
}
/// <inheritdoc/>
public virtual IDisposable Subscribe(Type eventType, IEventHandler handler)
{
return Subscribe(eventType, new SingleInstanceHandlerFactory(handler));
}
/// <inheritdoc/>
public virtual IDisposable Subscribe<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
return Subscribe(typeof(TEvent), factory);
}
public abstract IDisposable Subscribe(Type eventType, IEventHandlerFactory factory);
public abstract void Unsubscribe<TEvent>(Func<TEvent, Task> action) where TEvent : class;
/// <inheritdoc/>
public virtual void Unsubscribe<TEvent>(ILocalEventHandler<TEvent> handler) where TEvent : class
{
Unsubscribe(typeof(TEvent), handler);
}
public abstract void Unsubscribe(Type eventType, IEventHandler handler);
/// <inheritdoc/>
public virtual void Unsubscribe<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
Unsubscribe(typeof(TEvent), factory);
}
public abstract void Unsubscribe(Type eventType, IEventHandlerFactory factory);
/// <inheritdoc/>
public virtual void UnsubscribeAll<TEvent>() where TEvent : class
{
UnsubscribeAll(typeof(TEvent));
}
/// <inheritdoc/>
public abstract void UnsubscribeAll(Type eventType);
/// <inheritdoc/>
public virtual Task PublishAsync<TEvent>(TEvent eventData) where TEvent : class
{
return PublishAsync(typeof(TEvent), eventData);
}
/// <inheritdoc/>
public abstract Task PublishAsync(Type eventType, object eventData);
public virtual async Task TriggerHandlersAsync(Type eventType, object eventData, Action<EventExecutionErrorContext> onErrorAction = null)
{
var exceptions = new List<Exception>();
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<Exception> 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<IEventHandler> 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<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType);
protected virtual async Task TriggerHandlerAsync(IEventHandlerFactory asyncHandlerFactory, Type eventType, object eventData, List<Exception> 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<object>.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<object>.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<IEventHandlerFactory> EventHandlerFactories { get; }
public EventTypeWithEventHandlerFactories(Type eventType, List<IEventHandlerFactory> 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()
{
}
}
}
}