diff --git a/Volo.Abp.sln b/Volo.Abp.sln
index ff5187c3df..6e5a711457 100644
--- a/Volo.Abp.sln
+++ b/Volo.Abp.sln
@@ -276,6 +276,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Abp.Permissions.Applic
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.Caching", "src\Volo.Abp.Caching\Volo.Abp.Caching.csproj", "{A5B650AB-A67F-4A4C-9F81-7B5471CA1331}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Volo.Abp.EventBus", "src\Volo.Abp.EventBus\Volo.Abp.EventBus.csproj", "{D9455AE7-2E0C-4647-9880-F5831BCEE3D8}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -722,6 +724,10 @@ Global
{A5B650AB-A67F-4A4C-9F81-7B5471CA1331}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5B650AB-A67F-4A4C-9F81-7B5471CA1331}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5B650AB-A67F-4A4C-9F81-7B5471CA1331}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D9455AE7-2E0C-4647-9880-F5831BCEE3D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D9455AE7-2E0C-4647-9880-F5831BCEE3D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D9455AE7-2E0C-4647-9880-F5831BCEE3D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D9455AE7-2E0C-4647-9880-F5831BCEE3D8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -856,6 +862,7 @@ Global
{837ABFF5-66EE-486A-8023-8DD3EE7D7D96} = {055F4AC0-46C4-4E99-89CD-0F30D4A97382}
{90197499-CBB6-4C8C-90E6-8718CD755C04} = {055F4AC0-46C4-4E99-89CD-0F30D4A97382}
{A5B650AB-A67F-4A4C-9F81-7B5471CA1331} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8}
+ {D9455AE7-2E0C-4647-9880-F5831BCEE3D8} = {4C753F64-0C93-4D65-96C2-A40893AFC1E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BB97ECF4-9A84-433F-A80B-2A3285BDD1D5}
diff --git a/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj b/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj
new file mode 100644
index 0000000000..e47bcce0d9
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo.Abp.EventBus.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+ netstandard2.0
+ Volo.Abp.EventBus
+ Volo.Abp.EventBus
+ $(AssetTargetFallback);portable-net45+win8+wp8+wpa81;
+ false
+ false
+ false
+
+
+
+
+
+
+
+
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs
new file mode 100644
index 0000000000..2eabf6db80
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs
@@ -0,0 +1,13 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Modularity;
+
+namespace Volo.Abp.EventBus
+{
+ public class AbpEventBusModule : AbpModule
+ {
+ public override void ConfigureServices(IServiceCollection services)
+ {
+ services.AddAssemblyOf();
+ }
+ }
+}
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBus.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBus.cs
new file mode 100644
index 0000000000..8472fddd1d
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBus.cs
@@ -0,0 +1,495 @@
+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 Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.EventBus.Factories;
+using Volo.Abp.EventBus.Factories.Internals;
+using Volo.Abp.EventBus.Handlers;
+using Volo.Abp.EventBus.Handlers.Internals;
+using Volo.Abp.Threading;
+
+namespace Volo.Abp.EventBus
+{
+ ///
+ /// Implements EventBus as Singleton pattern.
+ ///
+ public class EventBus : IEventBus, ISingletonDependency
+ {
+ ///
+ /// Gets the default instance.
+ ///
+ public static EventBus Instance { 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 : class
+ {
+ return Register(typeof(TEventData), new ActionEventHandler(action));
+ }
+
+ ///
+ public IDisposable AsyncRegister(Func action) where TEventData : class
+ {
+ return Register(typeof(TEventData), new AsyncActionEventHandler(action));
+ }
+
+ ///
+ public IDisposable Register(IEventHandler handler) where TEventData : class
+ {
+ return Register(typeof(TEventData), handler);
+ }
+
+ ///
+ public IDisposable AsyncRegister(IAsyncEventHandler handler) where TEventData : class
+ {
+ return Register(typeof(TEventData), handler);
+ }
+
+ ///
+ public IDisposable Register()
+ where TEventData : class
+ 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 : class
+ {
+ 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 : class
+ {
+ 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 : class
+ {
+ 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 : class
+ {
+ Unregister(typeof(TEventData), handler);
+ }
+
+ ///
+ public void AsyncUnregister(IAsyncEventHandler handler) where TEventData : class
+ {
+ 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 : class
+ {
+ Unregister(typeof(TEventData), factory);
+ }
+
+ ///
+ public void Unregister(Type eventType, IEventHandlerFactory factory)
+ {
+ GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory));
+ }
+
+ ///
+ public void UnregisterAll() where TEventData : class
+ {
+ UnregisterAll(typeof(TEventData));
+ }
+
+ ///
+ public void UnregisterAll(Type eventType)
+ {
+ GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear());
+ }
+
+ ///
+ public void Trigger(TEventData eventData) where TEventData : class
+ {
+ Trigger(typeof(TEventData), eventData);
+ }
+
+ ///
+ public void Trigger(Type eventType, object eventData)
+ {
+ var exceptions = new List();
+
+ 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 classWithInheritableGenericArgument
+ 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);
+ Trigger(baseEventType, 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 : class
+ {
+ return TriggerAsync(typeof(TEventData), eventData);
+ }
+
+ ///
+ public async Task TriggerAsync(Type eventType, object eventData)
+ {
+ var exceptions = new List();
+
+ 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 classWithInheritableGenericArgument
+ 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 TriggerAsync(baseEventType, 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, object 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[] { 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, object 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[] { 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()
+ {
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/IEventHandlerFactory.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/IEventHandlerFactory.cs
new file mode 100644
index 0000000000..49604232a7
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/IEventHandlerFactory.cs
@@ -0,0 +1,29 @@
+using System;
+using Volo.Abp.EventBus.Handlers;
+
+namespace Volo.Abp.EventBus.Factories
+{
+ ///
+ /// Defines an interface for factories those are responsible to create/get and release of event handlers.
+ ///
+ public interface IEventHandlerFactory
+ {
+ ///
+ /// Gets an event handler.
+ ///
+ /// The event handler
+ IEventHandler GetHandler();
+
+ ///
+ /// Gets type of the handler (without creating an instance).
+ ///
+ ///
+ Type GetHandlerType();
+
+ ///
+ /// Releases an event handler.
+ ///
+ /// Handle to be released
+ void ReleaseHandler(IEventHandler handler);
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/Internals/FactoryUnregistrar.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/Internals/FactoryUnregistrar.cs
new file mode 100644
index 0000000000..47f3eca73b
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/Internals/FactoryUnregistrar.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace Volo.Abp.EventBus.Factories.Internals
+{
+ ///
+ /// Used to unregister a on method.
+ ///
+ internal class FactoryUnregistrar : IDisposable
+ {
+ private readonly IEventBus _eventBus;
+ private readonly Type _eventType;
+ private readonly IEventHandlerFactory _factory;
+
+ public FactoryUnregistrar(IEventBus eventBus, Type eventType, IEventHandlerFactory factory)
+ {
+ _eventBus = eventBus;
+ _eventType = eventType;
+ _factory = factory;
+ }
+
+ public void Dispose()
+ {
+ _eventBus.Unregister(_eventType, _factory);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/Internals/SingleInstanceHandlerFactory.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/Internals/SingleInstanceHandlerFactory.cs
new file mode 100644
index 0000000000..5fa2cbfb11
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/Internals/SingleInstanceHandlerFactory.cs
@@ -0,0 +1,45 @@
+using System;
+using Volo.Abp.DynamicProxy;
+using Volo.Abp.EventBus.Handlers;
+
+namespace Volo.Abp.EventBus.Factories.Internals
+{
+ ///
+ /// This implementation is used to handle events
+ /// by a single instance object.
+ ///
+ ///
+ /// This class always gets the same single instance of handler.
+ ///
+ internal class SingleInstanceHandlerFactory : IEventHandlerFactory
+ {
+ ///
+ /// The event handler instance.
+ ///
+ public IEventHandler HandlerInstance { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ public SingleInstanceHandlerFactory(IEventHandler handler)
+ {
+ HandlerInstance = handler;
+ }
+
+ public IEventHandler GetHandler()
+ {
+ return HandlerInstance;
+ }
+
+ public Type GetHandlerType()
+ {
+ return ProxyHelper.UnProxy(HandlerInstance).GetType();
+ }
+
+ public void ReleaseHandler(IEventHandler handler)
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/Internals/TransientEventHandlerFactory.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/Internals/TransientEventHandlerFactory.cs
new file mode 100644
index 0000000000..67adc41f90
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/Internals/TransientEventHandlerFactory.cs
@@ -0,0 +1,42 @@
+using System;
+using Volo.Abp.EventBus.Handlers;
+
+namespace Volo.Abp.EventBus.Factories.Internals
+{
+ ///
+ /// This implementation is used to handle events
+ /// by a transient instance object.
+ ///
+ ///
+ /// This class always creates a new transient instance of handler.
+ ///
+ internal class TransientEventHandlerFactory : IEventHandlerFactory
+ where THandler : IEventHandler, new()
+ {
+ ///
+ /// Creates a new instance of the handler object.
+ ///
+ /// The handler object
+ public IEventHandler GetHandler()
+ {
+ return new THandler();
+ }
+
+ public Type GetHandlerType()
+ {
+ return typeof(THandler);
+ }
+
+ ///
+ /// Disposes the handler object if it's . Does nothing if it's not.
+ ///
+ /// Handler to be released
+ public void ReleaseHandler(IEventHandler handler)
+ {
+ if (handler is IDisposable)
+ {
+ (handler as IDisposable).Dispose();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/IocHandlerFactory.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/IocHandlerFactory.cs
new file mode 100644
index 0000000000..36b220bb2f
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Factories/IocHandlerFactory.cs
@@ -0,0 +1,55 @@
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.EventBus.Handlers;
+
+namespace Volo.Abp.EventBus.Factories
+{
+ ///
+ /// This implementation is used to get/release
+ /// handlers using Ioc.
+ ///
+ public class IocHandlerFactory : IEventHandlerFactory
+ {
+ ///
+ /// Type of the handler.
+ ///
+ public Type HandlerType { get; }
+
+ private readonly IServiceProvider _iocResolver;
+
+ ///
+ /// Creates a new instance of class.
+ ///
+ ///
+ /// Type of the handler
+ public IocHandlerFactory(IServiceProvider iocResolver, Type handlerType)
+ {
+ _iocResolver = iocResolver;
+ HandlerType = handlerType;
+ }
+
+ ///
+ /// Resolves handler object from Ioc container.
+ ///
+ /// Resolved handler object
+ public IEventHandler GetHandler()
+ {
+ return (IEventHandler)_iocResolver.GetRequiredService(HandlerType);
+ }
+
+ public Type GetHandlerType()
+ {
+ return HandlerType;
+ }
+
+ ///
+ /// Releases handler object using Ioc container.
+ ///
+ /// Handler to be released
+ public void ReleaseHandler(IEventHandler handler)
+ {
+ //TODO: Scope!!!
+ //_iocResolver.Release(handler);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/IAsyncEventHandlerOfTEventData.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/IAsyncEventHandlerOfTEventData.cs
new file mode 100644
index 0000000000..e2aa3b3216
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/IAsyncEventHandlerOfTEventData.cs
@@ -0,0 +1,17 @@
+using System.Threading.Tasks;
+
+namespace Volo.Abp.EventBus.Handlers
+{
+ ///
+ /// Defines an interface of a class that handles events asynchrounously of type .
+ ///
+ /// Event type to handle
+ public interface IAsyncEventHandler : IEventHandler
+ {
+ ///
+ /// Handler handles the event by implementing this method.
+ ///
+ /// Event data
+ Task HandleEventAsync(TEventData eventData);
+ }
+}
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/IEventHandler.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/IEventHandler.cs
new file mode 100644
index 0000000000..2e38fc7ac3
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/IEventHandler.cs
@@ -0,0 +1,11 @@
+namespace Volo.Abp.EventBus.Handlers
+{
+ ///
+ /// Undirect base interface for all event handlers.
+ /// Implement instead of this one.
+ ///
+ public interface IEventHandler
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/IEventHandlerOfTEventData.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/IEventHandlerOfTEventData.cs
new file mode 100644
index 0000000000..38ec4eff7a
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/IEventHandlerOfTEventData.cs
@@ -0,0 +1,15 @@
+namespace Volo.Abp.EventBus.Handlers
+{
+ ///
+ /// Defines an interface of a class that handles events of type .
+ ///
+ /// Event type to handle
+ public interface IEventHandler : IEventHandler
+ {
+ ///
+ /// Handler handles the event by implementing this method.
+ ///
+ /// Event data
+ void HandleEvent(TEventData eventData);
+ }
+}
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/Internals/ActionEventHandler.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/Internals/ActionEventHandler.cs
new file mode 100644
index 0000000000..54e3cd95b8
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/Internals/ActionEventHandler.cs
@@ -0,0 +1,37 @@
+using System;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.EventBus.Handlers.Internals
+{
+ ///
+ /// This event handler is an adapter to be able to use an action as implementation.
+ ///
+ /// Event type
+ internal class ActionEventHandler :
+ IEventHandler,
+ ITransientDependency
+ {
+ ///
+ /// Action to handle the event.
+ ///
+ public Action Action { get; private set; }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// Action to handle the event
+ public ActionEventHandler(Action handler)
+ {
+ Action = handler;
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ ///
+ public void HandleEvent(TEventData eventData)
+ {
+ Action(eventData);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/Internals/AsyncActionEventHandler.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/Internals/AsyncActionEventHandler.cs
new file mode 100644
index 0000000000..9183c41a05
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Handlers/Internals/AsyncActionEventHandler.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.EventBus.Handlers.Internals
+{
+ ///
+ /// This event handler is an adapter to be able to use an action as implementation.
+ ///
+ /// Event type
+ internal class AsyncActionEventHandler :
+ IAsyncEventHandler,
+ ITransientDependency
+ {
+ ///
+ /// Function to handle the event.
+ ///
+ public Func Action { get; private set; }
+
+ ///
+ /// Creates a new instance of .
+ ///
+ /// Action to handle the event
+ public AsyncActionEventHandler(Func handler)
+ {
+ Action = handler;
+ }
+
+ ///
+ /// Handles the event.
+ ///
+ ///
+ public async Task HandleEventAsync(TEventData eventData)
+ {
+ await Action(eventData);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IDistributedEventBus.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IDistributedEventBus.cs
new file mode 100644
index 0000000000..4b81b1ee4a
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IDistributedEventBus.cs
@@ -0,0 +1,7 @@
+namespace Volo.Abp.EventBus
+{
+ public interface IDistributedEventBus
+ {
+
+ }
+}
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventBus.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventBus.cs
new file mode 100644
index 0000000000..5f8270a22d
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventBus.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.EventBus.Factories;
+using Volo.Abp.EventBus.Handlers;
+
+namespace Volo.Abp.EventBus
+{
+ ///
+ /// Defines interface of the event bus.
+ ///
+ public interface IEventBus
+ {
+ #region Register
+
+ ///
+ /// Registers to an event.
+ /// Given action is called for all event occurrences.
+ ///
+ /// Action to handle events
+ /// Event type
+ IDisposable Register(Action action)
+ where TEventData : class;
+
+ ///
+ /// Registers to an event.
+ /// Given action is called for all event occurrences.
+ ///
+ /// Action to handle events
+ /// Event type
+ IDisposable AsyncRegister(Func action)
+ where TEventData : class;
+
+ ///
+ /// Registers to an event.
+ /// Same (given) instance of the handler is used for all event occurrences.
+ ///
+ /// Event type
+ /// Object to handle the event
+ IDisposable Register(IEventHandler handler)
+ where TEventData : class;
+
+ ///
+ /// Registers to an event.
+ /// Same (given) instance of the async handler is used for all event occurrences.
+ ///
+ /// Event type
+ /// Object to handle the event
+ IDisposable AsyncRegister(IAsyncEventHandler handler)
+ where TEventData : class;
+
+ ///
+ /// Registers to an event.
+ /// A new instance of object is created for every event occurrence.
+ ///
+ /// Event type
+ /// Type of the event handler
+ IDisposable Register()
+ where TEventData : class
+ where THandler : IEventHandler, new();
+
+ ///
+ /// Registers to an event.
+ /// Same (given) instance of the handler is used for all event occurrences.
+ ///
+ /// Event type
+ /// Object to handle the event
+ IDisposable Register(Type eventType, IEventHandler handler);
+
+ ///
+ /// Registers to an event.
+ /// Given factory is used to create/release handlers
+ ///
+ /// Event type
+ /// A factory to create/release handlers
+ IDisposable Register(IEventHandlerFactory factory)
+ where TEventData : class;
+
+ ///
+ /// Registers to an event.
+ ///
+ /// Event type
+ /// A factory to create/release handlers
+ IDisposable Register(Type eventType, IEventHandlerFactory factory);
+
+ #endregion
+
+ #region Unregister
+
+ ///
+ /// Unregisters from an event.
+ ///
+ /// Event type
+ ///
+ void Unregister(Action action)
+ where TEventData : class;
+
+ ///
+ /// Unregisters from an event.
+ ///
+ /// Event type
+ ///
+ void AsyncUnregister(Func action)
+ where TEventData : class;
+
+ ///
+ /// Unregisters from an event.
+ ///
+ /// Event type
+ /// Handler object that is registered before
+ void Unregister(IEventHandler handler)
+ where TEventData : class;
+
+ ///
+ /// Unregisters from an event.
+ ///
+ /// Event type
+ /// Handler object that is registered before
+ void AsyncUnregister(IAsyncEventHandler handler)
+ where TEventData : class;
+
+ ///
+ /// Unregisters from an event.
+ ///
+ /// Event type
+ /// Handler object that is registered before
+ void Unregister(Type eventType, IEventHandler handler);
+
+ ///
+ /// Unregisters from an event.
+ ///
+ /// Event type
+ /// Factory object that is registered before
+ void Unregister(IEventHandlerFactory factory)
+ where TEventData : class;
+
+ ///
+ /// Unregisters from an event.
+ ///
+ /// Event type
+ /// Factory object that is registered before
+ void Unregister(Type eventType, IEventHandlerFactory factory);
+
+ ///
+ /// Unregisters all event handlers of given event type.
+ ///
+ /// Event type
+ void UnregisterAll()
+ where TEventData : class;
+
+ ///
+ /// Unregisters all event handlers of given event type.
+ ///
+ /// Event type
+ void UnregisterAll(Type eventType);
+
+ #endregion
+
+ #region Trigger
+
+ ///
+ /// Triggers an event.
+ ///
+ /// Event type
+ /// Related data for the event
+ void Trigger(TEventData eventData)
+ where TEventData : class;
+
+ ///
+ /// Triggers an event.
+ ///
+ /// Event type
+ /// Related data for the event
+ void Trigger(Type eventType, object eventData);
+
+ ///
+ /// Triggers an event asynchronously.
+ ///
+ /// Event type
+ /// Related data for the event
+ /// The task to handle async operation
+ Task TriggerAsync(TEventData eventData)
+ where TEventData : class;
+
+ ///
+ /// Triggers an event asynchronously.
+ ///
+ /// Event type
+ /// Related data for the event
+ /// The task to handle async operation
+ Task TriggerAsync(Type eventType, object eventData);
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventDataWithInheritableGenericArgument.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventDataWithInheritableGenericArgument.cs
new file mode 100644
index 0000000000..77fa399e58
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventDataWithInheritableGenericArgument.cs
@@ -0,0 +1,20 @@
+namespace Volo.Abp.EventBus
+{
+ ///
+ /// This interface must be implemented by event data classes that
+ /// has a single generic argument and this argument will be used by inheritance.
+ ///
+ /// For example;
+ /// Assume that Student inherits From Person. When trigger an EntityCreatedEventData{Student},
+ /// EntityCreatedEventData{Person} is also triggered if EntityCreatedEventData implements
+ /// this interface.
+ ///
+ public interface IEventDataWithInheritableGenericArgument
+ {
+ ///
+ /// Gets arguments to create this class since a new instance of this class is created.
+ ///
+ /// Constructor arguments
+ object[] GetConstructorArgs();
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.EventBus/Volo/Abp/EventBus/NullEventBus.cs b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/NullEventBus.cs
new file mode 100644
index 0000000000..230e1959c0
--- /dev/null
+++ b/src/Volo.Abp.EventBus/Volo/Abp/EventBus/NullEventBus.cs
@@ -0,0 +1,122 @@
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.EventBus.Factories;
+using Volo.Abp.EventBus.Handlers;
+
+namespace Volo.Abp.EventBus
+{
+ public sealed class NullEventBus : IEventBus
+ {
+ public static NullEventBus Instance { get; } = new NullEventBus();
+
+ private NullEventBus()
+ {
+
+ }
+
+ public IDisposable Register(Action action) where TEventData : class
+ {
+ return NullDisposable.Instance;
+ }
+
+ public IDisposable AsyncRegister(Func action) where TEventData : class
+ {
+ return NullDisposable.Instance;
+ }
+
+ public IDisposable Register(IEventHandler handler) where TEventData : class
+ {
+ return NullDisposable.Instance;
+ }
+
+ public IDisposable AsyncRegister(IAsyncEventHandler handler) where TEventData : class
+ {
+ return NullDisposable.Instance;
+ }
+
+ public IDisposable Register() where TEventData : class where THandler : IEventHandler, new()
+ {
+ return NullDisposable.Instance;
+ }
+
+ public IDisposable Register(Type eventType, IEventHandler handler)
+ {
+ return NullDisposable.Instance;
+ }
+
+ public IDisposable Register(IEventHandlerFactory factory) where TEventData : class
+ {
+ return NullDisposable.Instance;
+ }
+
+ public IDisposable Register(Type eventType, IEventHandlerFactory factory)
+ {
+ return NullDisposable.Instance;
+ }
+
+ public void Unregister(Action action) where TEventData : class
+ {
+
+ }
+
+ public void AsyncUnregister(Func action) where TEventData : class
+ {
+
+ }
+
+ public void Unregister(IEventHandler handler) where TEventData : class
+ {
+
+ }
+
+ public void AsyncUnregister(IAsyncEventHandler handler) where TEventData : class
+ {
+
+ }
+
+ public void Unregister(Type eventType, IEventHandler handler)
+ {
+
+ }
+
+ public void Unregister(IEventHandlerFactory factory) where TEventData : class
+ {
+
+ }
+
+ public void Unregister(Type eventType, IEventHandlerFactory factory)
+ {
+
+ }
+
+ public void UnregisterAll() where TEventData : class
+ {
+
+ }
+
+ public void UnregisterAll(Type eventType)
+ {
+
+ }
+
+ public void Trigger(TEventData eventData) where TEventData : class
+ {
+
+ }
+
+ public void Trigger(Type eventType, object eventData)
+ {
+
+ }
+
+ public Task TriggerAsync(TEventData eventData) where TEventData : class
+ {
+ return Task.CompletedTask;
+ }
+
+ public Task TriggerAsync(Type eventType, object eventData)
+ {
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionStore.cs b/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionStore.cs
index ca67c9006c..afd79ebd20 100644
--- a/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionStore.cs
+++ b/src/Volo.Abp.Permissions.Domain/Volo/Abp/Permissions/PermissionStore.cs
@@ -9,12 +9,13 @@ using Volo.Abp.Threading;
namespace Volo.Abp.Permissions
{
- /* TODOS:
- * - Wrap distributed cache?
+ /* TODO:
+ * - Cache Invalidation (via entity events - eventbus)
+ * - Wrap distributed cache
* - Add multi-tenancy
* - Add _cancellationTokenProvider support
* - Add object serialization support
- * - Add cache invalidation support..? Maybe it's not cache's job!
+ * - Wrap in-memory cache!
*/
public class PermissionStore : AbpServiceBase, IPermissionStore, ITransientDependency
@@ -47,11 +48,11 @@ namespace Volo.Abp.Permissions
private string CalculateCacheKey(string name, string providerName, string providerKey)
{
- var key = "P:" + providerName + "_K:" + providerKey + "N:" + name;
+ var key = "P:" + providerName + "#K:" + providerKey + "#N:" + name;
if (_currentTenant.Id.HasValue)
{
- key = "T:" + _currentTenant.Id + "_" + key;
+ key = "T:" + _currentTenant.Id + "#" + key;
}
return key;