using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Abp.Events.Bus.Factories;
using Abp.Events.Bus.Factories.Internals;
using Abp.Events.Bus.Handlers;
using Abp.Events.Bus.Handlers.Internals;
using Abp.Extensions;
using Abp.Threading;
using Abp.Threading.Extensions;
using Castle.Core.Logging;
namespace Abp.Events.Bus
{
///
/// Implements EventBus as Singleton pattern.
///
public class EventBus : IEventBus
{
///
/// Gets the default instance.
///
public static EventBus Default { get; } = new EventBus();
///
/// Reference to the Logger.
///
public ILogger Logger { get; set; }
///
/// All registered handler factories.
/// Key: Type of the event
/// Value: List of handler factories
///
private readonly ConcurrentDictionary> _handlerFactories;
///
/// Creates a new instance.
/// Instead of creating a new instace, you can use to use Global .
///
public EventBus()
{
_handlerFactories = new ConcurrentDictionary>();
Logger = NullLogger.Instance;
}
///
public IDisposable Register(Action action) where TEventData : IEventData
{
return Register(typeof(TEventData), new ActionEventHandler(action));
}
///
public IDisposable AsyncRegister(Func action) where TEventData : IEventData
{
return Register(typeof(TEventData), new AsyncActionEventHandler(action));
}
///
public IDisposable Register(IEventHandler handler) where TEventData : IEventData
{
return Register(typeof(TEventData), handler);
}
///
public IDisposable AsyncRegister(IAsyncEventHandler handler) where TEventData : IEventData
{
return Register(typeof(TEventData), handler);
}
///
public IDisposable Register()
where TEventData : IEventData
where THandler : IEventHandler, new()
{
return Register(typeof(TEventData), new TransientEventHandlerFactory());
}
///
public IDisposable Register(Type eventType, IEventHandler handler)
{
return Register(eventType, new SingleInstanceHandlerFactory(handler));
}
///
public IDisposable Register(IEventHandlerFactory factory) where TEventData : IEventData
{
return Register(typeof(TEventData), factory);
}
///
public IDisposable Register(Type eventType, IEventHandlerFactory factory)
{
GetOrCreateHandlerFactories(eventType)
.Locking(factories => factories.Add(factory));
return new FactoryUnregistrar(this, eventType, factory);
}
///
public void Unregister(Action action) where TEventData : IEventData
{
Check.NotNull(action, nameof(action));
GetOrCreateHandlerFactories(typeof(TEventData))
.Locking(factories =>
{
factories.RemoveAll(
factory =>
{
var singleInstanceFactory = factory as SingleInstanceHandlerFactory;
if (singleInstanceFactory == null)
{
return false;
}
var actionHandler = singleInstanceFactory.HandlerInstance as ActionEventHandler;
if (actionHandler == null)
{
return false;
}
return actionHandler.Action == action;
});
});
}
///
public void AsyncUnregister(Func action) where TEventData : IEventData
{
Check.NotNull(action, nameof(action));
GetOrCreateHandlerFactories(typeof(TEventData))
.Locking(factories =>
{
factories.RemoveAll(
factory =>
{
var singleInstanceFactory = factory as SingleInstanceHandlerFactory;
if (singleInstanceFactory == null)
{
return false;
}
var actionHandler = singleInstanceFactory.HandlerInstance as AsyncActionEventHandler;
if (actionHandler == null)
{
return false;
}
return actionHandler.Action == action;
});
});
}
///
public void Unregister(IEventHandler handler) where TEventData : IEventData
{
Unregister(typeof(TEventData), handler);
}
///
public void AsyncUnregister(IAsyncEventHandler handler) where TEventData : IEventData
{
Unregister(typeof(TEventData), handler);
}
///
public void Unregister(Type eventType, IEventHandler handler)
{
GetOrCreateHandlerFactories(eventType)
.Locking(factories =>
{
factories.RemoveAll(
factory =>
factory is SingleInstanceHandlerFactory &&
(factory as SingleInstanceHandlerFactory).HandlerInstance == handler
);
});
}
///
public void Unregister(IEventHandlerFactory factory) where TEventData : IEventData
{
Unregister(typeof(TEventData), factory);
}
///
public void Unregister(Type eventType, IEventHandlerFactory factory)
{
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory));
}
///
public void UnregisterAll() where TEventData : IEventData
{
UnregisterAll(typeof(TEventData));
}
///
public void UnregisterAll(Type eventType)
{
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear());
}
///
public void Trigger(TEventData eventData) where TEventData : IEventData
{
Trigger((object)null, eventData);
}
///
public void Trigger(object eventSource, TEventData eventData) where TEventData : IEventData
{
Trigger(typeof(TEventData), eventSource, eventData);
}
///
public void Trigger(Type eventType, IEventData eventData)
{
Trigger(eventType, null, eventData);
}
///
public void Trigger(Type eventType, object eventSource, IEventData eventData)
{
var exceptions = new List();
eventData.EventSource = eventSource;
foreach (var handlerFactories in GetHandlerFactories(eventType))
{
foreach (var handlerFactory in handlerFactories.EventHandlerFactories)
{
var handlerType = handlerFactory.GetHandlerType();
if (IsAsyncEventHandler(handlerType))
{
AsyncHelper.RunSync(() => TriggerAsyncHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions));
}
else if (IsEventHandler(handlerType))
{
TriggerHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions);
}
else
{
var message = $"Event handler to register for event type {eventType.Name} does not implement IEventHandler<{eventType.Name}> or IAsyncEventHandler<{eventType.Name}> interface!";
exceptions.Add(new AbpException(message));
}
}
}
//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 = (IEventData)Activator.CreateInstance(baseEventType, constructorArgs);
baseEventData.EventTime = eventData.EventTime;
Trigger(baseEventType, eventData.EventSource, baseEventData);
}
}
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);
}
}
///
public Task TriggerAsync(TEventData eventData) where TEventData : IEventData
{
return TriggerAsync((object)null, eventData);
}
///
public Task TriggerAsync(object eventSource, TEventData eventData) where TEventData : IEventData
{
return TriggerAsync(typeof(TEventData), eventSource, eventData);
}
///
public Task TriggerAsync(Type eventType, IEventData eventData)
{
return TriggerAsync(eventType, null, eventData);
}
///
public async Task TriggerAsync(Type eventType, object eventSource, IEventData eventData)
{
var exceptions = new List();
eventData.EventSource = eventSource;
await new SynchronizationContextRemover();
foreach (var handlerFactories in GetHandlerFactories(eventType))
{
foreach (var handlerFactory in handlerFactories.EventHandlerFactories)
{
var handlerType = handlerFactory.GetHandlerType();
if (IsAsyncEventHandler(handlerType))
{
await TriggerAsyncHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions);
}
else if (IsEventHandler(handlerType))
{
TriggerHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions);
}
else
{
var message = $"Event handler to register for event type {eventType.Name} does not implement IEventHandler<{eventType.Name}> or IAsyncEventHandler<{eventType.Name}> interface!";
exceptions.Add(new AbpException(message));
}
}
}
//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 = (IEventData)Activator.CreateInstance(baseEventType, constructorArgs);
baseEventData.EventTime = eventData.EventTime;
await TriggerAsync(baseEventType, eventData.EventSource, baseEventData);
}
}
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);
}
}
private void TriggerHandlingException(IEventHandlerFactory handlerFactory, Type eventType, IEventData eventData, List exceptions)
{
var eventHandler = handlerFactory.GetHandler();
try
{
if (eventHandler == null)
{
throw new ArgumentNullException($"Registered event handler for event type {eventType.Name} is null!");
}
var handlerType = typeof(IEventHandler<>).MakeGenericType(eventType);
var method = handlerType.GetMethod(
"HandleEvent",
new[] { eventType }
);
method.Invoke(eventHandler, new object[] { eventData });
}
catch (TargetInvocationException ex)
{
exceptions.Add(ex.InnerException);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
finally
{
handlerFactory.ReleaseHandler(eventHandler);
}
}
private async Task TriggerAsyncHandlingException(IEventHandlerFactory asyncHandlerFactory, Type eventType, IEventData eventData, List exceptions)
{
var asyncEventHandler = asyncHandlerFactory.GetHandler();
try
{
if (asyncEventHandler == null)
{
throw new ArgumentNullException($"Registered async event handler for event type {eventType.Name} is null!");
}
var asyncHandlerType = typeof(IAsyncEventHandler<>).MakeGenericType(eventType);
var method = asyncHandlerType.GetMethod(
"HandleEventAsync",
new[] { eventType }
);
await (Task)method.Invoke(asyncEventHandler, new object[] { eventData });
}
catch (TargetInvocationException ex)
{
exceptions.Add(ex.InnerException);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
finally
{
asyncHandlerFactory.ReleaseHandler(asyncEventHandler);
}
}
private bool IsEventHandler(Type handlerType)
{
return handlerType.GetInterfaces()
.Where(i => i.IsGenericType)
.Any(i => i.GetGenericTypeDefinition() == typeof(IEventHandler<>));
}
private bool IsAsyncEventHandler(Type handlerType)
{
return handlerType.GetInterfaces()
.Where(i => i.IsGenericType)
.Any(i => i.GetGenericTypeDefinition() == typeof(IAsyncEventHandler<>));
}
private IEnumerable GetHandlerFactories(Type eventType)
{
var handlerFactoryList = new List();
foreach (var handlerFactory in _handlerFactories.Where(hf => ShouldTriggerEventForHandler(eventType, hf.Key)))
{
handlerFactoryList.Add(new EventTypeWithEventHandlerFactories(handlerFactory.Key, handlerFactory.Value));
}
return handlerFactoryList.ToArray();
}
private static bool ShouldTriggerEventForHandler(Type eventType, Type handlerType)
{
//Should trigger same type
if (handlerType == eventType)
{
return true;
}
//Should trigger for inherited types
if (handlerType.IsAssignableFrom(eventType))
{
return true;
}
return false;
}
private List GetOrCreateHandlerFactories(Type eventType)
{
return _handlerFactories.GetOrAdd(eventType, (type) => new List());
}
private 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/
private 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()
{
}
}
}
}