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;