diff --git a/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpCollectionExtensions.cs b/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpCollectionExtensions.cs
index 4240d895ba..69b4d795e0 100644
--- a/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpCollectionExtensions.cs
+++ b/framework/src/Volo.Abp.Core/System/Collections/Generic/AbpCollectionExtensions.cs
@@ -37,6 +37,33 @@ namespace System.Collections.Generic
return true;
}
+ ///
+ /// Adds items to the collection which are not already in the collection.
+ ///
+ /// The collection
+ /// Item to check and add
+ /// Type of the items in the collection
+ /// Returns the added items.
+ public static IEnumerable AddIfNotContains([NotNull] this ICollection source, IEnumerable items)
+ {
+ Check.NotNull(source, nameof(source));
+
+ var addedItems = new List();
+
+ foreach (var item in items)
+ {
+ if (source.Contains(item))
+ {
+ continue;
+ }
+
+ source.Add(item);
+ addedItems.Add(item);
+ }
+
+ return addedItems;
+ }
+
///
/// Adds an item to the collection if it's not already in the collection based on the given .
///
diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs
index 832f511eaa..c557013a03 100644
--- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs
@@ -5,6 +5,7 @@ using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.DynamicProxy;
using Volo.Abp.EventBus;
+using Volo.Abp.EventBus.Local;
using Volo.Abp.Uow;
namespace Volo.Abp.Domain.Entities.Events
@@ -14,14 +15,14 @@ namespace Volo.Abp.Domain.Entities.Events
///
public class EntityChangeEventHelper : IEntityChangeEventHelper, ITransientDependency
{
- public IEventBus EventBus { get; set; }
+ public ILocalEventBus LocalEventBus { get; set; }
private readonly IUnitOfWorkManager _unitOfWorkManager;
public EntityChangeEventHelper(IUnitOfWorkManager unitOfWorkManager)
{
_unitOfWorkManager = unitOfWorkManager;
- EventBus = NullEventBus.Instance;
+ LocalEventBus = NullLocalEventBus.Instance;
}
public async Task TriggerEventsAsync(EntityChangeReport changeReport)
@@ -115,7 +116,7 @@ namespace Volo.Abp.Domain.Entities.Events
{
foreach (var domainEvent in domainEvents)
{
- await EventBus.TriggerAsync(domainEvent.EventData.GetType(), domainEvent.EventData);
+ await LocalEventBus.PublishAsync(domainEvent.EventData.GetType(), domainEvent.EventData);
}
}
@@ -126,11 +127,11 @@ namespace Volo.Abp.Domain.Entities.Events
if (triggerInCurrentUnitOfWork || _unitOfWorkManager.Current == null)
{
- await EventBus.TriggerAsync(eventType, Activator.CreateInstance(eventType, entity));
+ await LocalEventBus.PublishAsync(eventType, Activator.CreateInstance(eventType, entity));
return;
}
- _unitOfWorkManager.Current.OnCompleted(() => EventBus.TriggerAsync(eventType, Activator.CreateInstance(eventType, entity)));
+ _unitOfWorkManager.Current.OnCompleted(() => LocalEventBus.PublishAsync(eventType, Activator.CreateInstance(eventType, entity)));
}
}
}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBus.cs
index 0976be74f4..053b41dc5b 100644
--- a/framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBus.cs
+++ b/framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBus.cs
@@ -1,83 +1,221 @@
using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.RabbitMQ;
using RabbitMQ.Client;
+using RabbitMQ.Client.Events;
+using Volo.Abp.Collections;
+using Volo.Abp.EventBus.Local;
+using Volo.Abp.Threading;
namespace Volo.Abp.EventBus.Distributed.RabbitMq
{
- /* Inspired from the implementation of "eShopOnContainers"
+ /* TODO: How to handle unsubscribe to unbind on RabbitMq (may not be possible for)
* TODO: Implement Retry system
* TODO: Should be improved
*/
- public class RabbitMqDistributedEventBus : IDistributedEventBus, ITransientDependency
+ [Dependency(ReplaceServices = true)]
+ [ExposeServices(typeof(IDistributedEventBus), typeof(RabbitMqDistributedEventBus))]
+ public class RabbitMqDistributedEventBus : EventBusBase, IDistributedEventBus, ISingletonDependency
{
- protected RabbitMqDistributedEventBusOptions Options { get; }
- protected IChannelPool ChannelPool { get; }
+ protected RabbitMqDistributedEventBusOptions RabbitMqDistributedEventBusOptions { get; }
+ protected DistributedEventBusOptions DistributedEventBusOptions { get; }
+ protected IConnectionPool ConnectionPool { get; }
protected IRabbitMqSerializer Serializer { get; }
+ protected ConcurrentDictionary> HandlerFactories { get; } //TODO: Accessing to the List may not be thread-safe!
+ protected ConcurrentDictionary EventTypes { get; }
+ protected IModel ConsumerChannel;
+ protected IServiceProvider ServiceProvider { get; }
public RabbitMqDistributedEventBus(
IOptions options,
- IChannelPool channelPool,
- IRabbitMqSerializer serializer)
+ IConnectionPool connectionPool,
+ IRabbitMqSerializer serializer,
+ IServiceProvider serviceProvider,
+ DistributedEventBusOptions distributedEventBusOptions)
{
- ChannelPool = channelPool;
+ ConnectionPool = connectionPool;
Serializer = serializer;
- Options = options.Value;
+ ServiceProvider = serviceProvider;
+ DistributedEventBusOptions = distributedEventBusOptions;
+ RabbitMqDistributedEventBusOptions = options.Value;
+
+ HandlerFactories = new ConcurrentDictionary>();
+ EventTypes = new ConcurrentDictionary();
+
+ ConsumerChannel = CreateConsumerChannel();
+ Subscribe(DistributedEventBusOptions.Handlers);
}
- public IDisposable Subscribe(Func action) where TEvent : class
+ public virtual void Subscribe(ITypeList handlers)
{
- throw new NotImplementedException();
+ 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(ServiceProvider, handler));
+ }
+ }
+ }
}
- public IDisposable Subscribe(IEventHandler handler) where TEvent : class
+ private IModel CreateConsumerChannel()
{
- throw new NotImplementedException();
+ //TODO: Support multiple connection (and consumer)?
+ var channel = ConnectionPool.Get().CreateModel();
+ channel.ExchangeDeclare(
+ exchange: RabbitMqDistributedEventBusOptions.ExchangeName,
+ type: "direct"
+ );
+
+ channel.QueueDeclare(
+ queue: RabbitMqDistributedEventBusOptions.ClientName,
+ durable: true,
+ exclusive: false,
+ autoDelete: false,
+ arguments: null
+ );
+
+ var consumer = new EventingBasicConsumer(channel);
+ consumer.Received += async (model, ea) => { await ProcessEventAsync(channel, ea); };
+
+ channel.BasicConsume(
+ queue: RabbitMqDistributedEventBusOptions.ClientName,
+ autoAck: false,
+ consumer: consumer
+ );
+
+ channel.CallbackException += (sender, ea) =>
+ {
+ ConsumerChannel.Dispose();
+ ConsumerChannel = CreateConsumerChannel();
+ };
+
+ return channel;
}
- public IDisposable Subscribe() where TEvent : class where THandler : IEventHandler, new()
+ private async Task ProcessEventAsync(IModel channel, BasicDeliverEventArgs ea)
{
- throw new NotImplementedException();
+ var eventName = ea.RoutingKey;
+ var eventType = EventTypes.GetOrDefault(eventName);
+ if (eventType == null)
+ {
+ return;
+ }
+
+ var eventData = Serializer.Deserialize(ea.Body, eventType);
+
+ await TriggerHandlersAsync(eventType, eventData);
+
+ channel.BasicAck(ea.DeliveryTag, multiple: false);
}
- public IDisposable Subscribe(Type eventType, IEventHandler handler)
+ public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
{
- throw new NotImplementedException();
+ var handlerFactories = GetOrCreateHandlerFactories(eventType);
+
+ handlerFactories.Add(factory);
+
+ if (handlerFactories.Count == 1) //TODO: Multi-threading!
+ {
+ var eventName = EventNameAttribute.GetName(eventType);
+
+ using (var channel = ConnectionPool.Get().CreateModel()) //TODO: Connection name per event!
+ {
+ channel.QueueBind(
+ queue: RabbitMqDistributedEventBusOptions.ClientName,
+ exchange: RabbitMqDistributedEventBusOptions.ExchangeName,
+ routingKey: eventName
+ );
+ }
+ }
+
+ return new EventHandlerFactoryUnregistrar(this, eventType, factory);
}
- public IDisposable Subscribe(IEventHandlerFactory factory) where TEvent : class
+ ///
+ public override void Unsubscribe(Func action)
{
- throw new NotImplementedException();
+ Check.NotNull(action, nameof(action));
+
+ GetOrCreateHandlerFactories(typeof(TEvent))
+ .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 IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
+ ///
+ public override void Unsubscribe(Type eventType, IEventHandler handler)
{
- throw new NotImplementedException();
+ GetOrCreateHandlerFactories(eventType)
+ .Locking(factories =>
+ {
+ factories.RemoveAll(
+ factory =>
+ factory is SingleInstanceHandlerFactory &&
+ (factory as SingleInstanceHandlerFactory).HandlerInstance == handler
+ );
+ });
}
- public Task PublishAsync(TEvent eventData)
- where TEvent : class
+ ///
+ public override void Unsubscribe(Type eventType, IEventHandlerFactory factory)
{
- return PublishAsync(typeof(TEvent), eventData);
+ GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory));
}
- public Task PublishAsync(Type eventType, object eventData)
+ ///
+ public override void UnsubscribeAll(Type eventType)
{
- var eventName = eventType.FullName; //TODO: Get eventname from an attribute if available
+ GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear());
+ }
+
+ public override Task PublishAsync(Type eventType, object eventData)
+ {
+ var eventName = EventNameAttribute.GetName(eventType);
var body = Serializer.Serialize(eventData);
- using (var channelAccessor = ChannelPool.Acquire(Guid.NewGuid().ToString()))
+ using (var channel = ConnectionPool.Get().CreateModel()) //TODO: Connection name per event!
{
//TODO: Other properties like durable?
- channelAccessor.Channel.ExchangeDeclare(Options.ExchangeName, "");
+ channel.ExchangeDeclare(RabbitMqDistributedEventBusOptions.ExchangeName, "");
- var properties = channelAccessor.Channel.CreateBasicProperties();
+ var properties = channel.CreateBasicProperties();
properties.DeliveryMode = 2; //persistent
- channelAccessor.Channel.BasicPublish(
- exchange: Options.ExchangeName,
+ channel.BasicPublish(
+ exchange: RabbitMqDistributedEventBusOptions.ExchangeName,
routingKey: eventName,
mandatory: true,
basicProperties: properties,
@@ -87,5 +225,48 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
return Task.CompletedTask;
}
+
+ private List GetOrCreateHandlerFactories(Type eventType)
+ {
+ return HandlerFactories.GetOrAdd(
+ eventType,
+ type =>
+ {
+ var eventName = EventNameAttribute.GetName(type);
+ EventTypes[eventName] = type;
+ return new List();
+ }
+ );
+ }
+
+ protected override 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 targetEventType, Type handlerEventType)
+ {
+ //Should trigger same type
+ if (handlerEventType == targetEventType)
+ {
+ return true;
+ }
+
+ //TODO: Support inheritance? But it does not support on subscription to RabbitMq!
+ //Should trigger for inherited types
+ if (handlerEventType.IsAssignableFrom(targetEventType))
+ {
+ return true;
+ }
+
+ return false;
+ }
}
}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBusOptions.cs b/framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBusOptions.cs
index a998861db4..fe277f8599 100644
--- a/framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBusOptions.cs
+++ b/framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBusOptions.cs
@@ -2,6 +2,8 @@
{
public class RabbitMqDistributedEventBusOptions
{
+ public string ClientName { get; set; }
+
public string ExchangeName { get; set; }
}
}
diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs
index 92c15a08d7..7b95c34a3d 100644
--- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs
+++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs
@@ -1,8 +1,11 @@
-using System;
+using Microsoft.Extensions.DependencyInjection;
+using System;
using System.Collections.Generic;
using System.Linq;
-using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.EventBus.Distributed;
+using Volo.Abp.EventBus.Local;
using Volo.Abp.Modularity;
+using Volo.Abp.Reflection;
namespace Volo.Abp.EventBus
{
@@ -15,22 +18,29 @@ namespace Volo.Abp.EventBus
private static void AddEventHandlers(IServiceCollection services)
{
- var handlers = new List();
+ var localHandlers = new List();
+ var distributedHandlers = new List();
services.OnRegistred(context =>
{
- if (context.ImplementationType.GetInterfaces().Any(i => typeof(IEventHandler).IsAssignableFrom(i)))
+ if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(IEventHandler<>)))
{
- handlers.Add(context.ImplementationType);
+ localHandlers.Add(context.ImplementationType);
+ }
+ else if (ReflectionHelper.IsAssignableToGenericType(context.ImplementationType, typeof(IDistributedEventHandler<>)))
+ {
+ distributedHandlers.Add(context.ImplementationType);
}
});
- services.Configure(options =>
+ services.Configure(options =>
{
- foreach (var handler in handlers)
- {
- options.Handlers.AddIfNotContains(handler);
- }
+ options.Handlers.AddIfNotContains(localHandlers);
+ });
+
+ services.Configure(options =>
+ {
+ options.Handlers.AddIfNotContains(distributedHandlers);
});
}
}
diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/ActionEventHandler.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/ActionEventHandler.cs
index 32b41ec800..995ce39356 100644
--- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/ActionEventHandler.cs
+++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/ActionEventHandler.cs
@@ -8,7 +8,7 @@ namespace Volo.Abp.EventBus
/// This event handler is an adapter to be able to use an action as implementation.
///
/// Event type
- internal class ActionEventHandler :
+ public class ActionEventHandler :
IEventHandler,
ITransientDependency
{
diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs
index 26bbe9cb1c..88cbf721b3 100644
--- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs
+++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs
@@ -1,65 +1,7 @@
-using System;
-using System.Threading.Tasks;
-
-namespace Volo.Abp.EventBus.Distributed
+namespace Volo.Abp.EventBus.Distributed
{
- public interface IDistributedEventBus
+ public interface IDistributedEventBus : IEventBus
{
- ///
- /// Subscribes to an event.
- /// Given action is called for all event occurrences.
- ///
- /// Action to handle events
- /// Event type
- IDisposable Subscribe(Func action)
- where TEvent : class;
-
- ///
- /// Subscribes to an event.
- /// Same (given) instance of the handler is used for all event occurrences.
- ///
- /// Event type
- /// Object to handle the event
- IDisposable Subscribe(IEventHandler handler)
- where TEvent : class;
-
- ///
- /// Subscribes to an event.
- /// A new instance of object is created for every event occurrence.
- ///
- /// Event type
- /// Type of the event handler
- IDisposable Subscribe()
- where TEvent : class
- where THandler : IEventHandler, new();
-
- ///
- /// Subscribes to an event.
- /// Same (given) instance of the handler is used for all event occurrences.
- ///
- /// Event type
- /// Object to handle the event
- IDisposable Subscribe(Type eventType, IEventHandler handler);
-
- ///
- /// Subscribes to an event.
- /// Given factory is used to create/release handlers
- ///
- /// Event type
- /// A factory to create/release handlers
- IDisposable Subscribe(IEventHandlerFactory factory)
- where TEvent : class;
-
- ///
- /// Subscribes to an event.
- ///
- /// Event type
- /// A factory to create/release handlers
- IDisposable Subscribe(Type eventType, IEventHandlerFactory factory);
-
- Task PublishAsync(TEvent eventData)
- where TEvent : class;
-
- Task PublishAsync(Type eventType, object eventData);
+
}
}
diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs
index 38c3687e48..ed5b0027cc 100644
--- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs
+++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs
@@ -1,58 +1,95 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
+using Volo.Abp.EventBus.Local;
namespace Volo.Abp.EventBus.Distributed
{
[Dependency(TryRegister = true)]
+ [ExposeServices(typeof(IDistributedEventBus), typeof(LocalDistributedEventBus))]
public class LocalDistributedEventBus : IDistributedEventBus, ITransientDependency
{
- private readonly IEventBus _eventBus;
+ private readonly ILocalEventBus _localEventBus;
- public LocalDistributedEventBus(IEventBus eventBus)
+ public LocalDistributedEventBus(ILocalEventBus localEventBus)
{
- _eventBus = eventBus;
+ _localEventBus = localEventBus;
}
public IDisposable Subscribe(Func action) where TEvent : class
{
- return _eventBus.Register(action);
+ return _localEventBus.Subscribe(action);
}
public IDisposable Subscribe(IEventHandler handler) where TEvent : class
{
- return _eventBus.Register(handler);
+ return _localEventBus.Subscribe(handler);
}
public IDisposable Subscribe() where TEvent : class where THandler : IEventHandler, new()
{
- return _eventBus.Register();
+ return _localEventBus.Subscribe();
}
public IDisposable Subscribe(Type eventType, IEventHandler handler)
{
- return _eventBus.Register(eventType, handler);
+ return _localEventBus.Subscribe(eventType, handler);
}
public IDisposable Subscribe(IEventHandlerFactory factory) where TEvent : class
{
- return _eventBus.Register(factory);
+ return _localEventBus.Subscribe(factory);
}
public IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
{
- return _eventBus.Register(eventType, factory);
+ return _localEventBus.Subscribe(eventType, factory);
+ }
+
+ public void Unsubscribe(Func action) where TEvent : class
+ {
+ _localEventBus.Unsubscribe(action);
+ }
+
+ public void Unsubscribe(IEventHandler handler) where TEvent : class
+ {
+ _localEventBus.Unsubscribe(handler);
+ }
+
+ public void Unsubscribe(Type eventType, IEventHandler handler)
+ {
+ _localEventBus.Unsubscribe(eventType, handler);
+ }
+
+ public void Unsubscribe(IEventHandlerFactory factory) where TEvent : class
+ {
+ _localEventBus.Unsubscribe(factory);
+ }
+
+ public void Unsubscribe(Type eventType, IEventHandlerFactory factory)
+ {
+ _localEventBus.Unsubscribe(eventType, factory);
+ }
+
+ public void UnsubscribeAll() where TEvent : class
+ {
+ _localEventBus.UnsubscribeAll();
+ }
+
+ public void UnsubscribeAll(Type eventType)
+ {
+ _localEventBus.UnsubscribeAll(eventType);
}
public Task PublishAsync(TEvent eventData)
where TEvent : class
{
- return _eventBus.TriggerAsync(eventData);
+ return _localEventBus.PublishAsync(eventData);
}
public Task PublishAsync(Type eventType, object eventData)
{
- return _eventBus.TriggerAsync(eventType, eventData);
+ return _localEventBus.PublishAsync(eventType, eventData);
}
}
}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalEventBusOptions.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalEventBusOptions.cs
new file mode 100644
index 0000000000..9e48bfa158
--- /dev/null
+++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalEventBusOptions.cs
@@ -0,0 +1,14 @@
+using Volo.Abp.Collections;
+
+namespace Volo.Abp.EventBus.Distributed
+{
+ public class DistributedEventBusOptions
+ {
+ public ITypeList Handlers { get; }
+
+ public DistributedEventBusOptions()
+ {
+ Handlers = new TypeList();
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBus.cs
deleted file mode 100644
index 3c7243c70e..0000000000
--- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBus.cs
+++ /dev/null
@@ -1,335 +0,0 @@
-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 Microsoft.Extensions.Options;
-using Volo.Abp.DependencyInjection;
-using Volo.Abp.Threading;
-
-namespace Volo.Abp.EventBus
-{
- ///
- /// Implements EventBus as Singleton pattern.
- ///
- public class EventBus : IEventBus, ISingletonDependency
- {
- ///
- /// Reference to the Logger.
- ///
- public ILogger Logger { get; set; }
-
- ///
- /// All registered handler factories.
- /// Key: Type of the event
- /// Value: List of handler factories
- ///
- protected ConcurrentDictionary> HandlerFactories { get; }
-
- protected EventBusOptions Options { get; }
-
- public EventBus(
- IOptions options,
- IServiceProvider serviceProvider)
- {
- Options = options.Value;
- Logger = NullLogger.Instance;
- HandlerFactories = new ConcurrentDictionary>();
-
- RegisterHandlersInOptions(serviceProvider);
- }
-
- protected virtual void RegisterHandlersInOptions(IServiceProvider serviceProvider)
- {
- foreach (var handler in Options.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)
- {
- Register(genericArgs[0], new IocEventHandlerFactory(serviceProvider, handler));
- }
- }
- }
- }
-
- ///
- public IDisposable Register(Func action) where TEvent : class
- {
- return Register(typeof(TEvent), new ActionEventHandler(action));
- }
-
- ///
- public IDisposable Register(IEventHandler handler) where TEvent : class
- {
- return Register(typeof(TEvent), handler);
- }
-
- ///
- public IDisposable Register()
- where TEvent : class
- where THandler : IEventHandler, new()
- {
- return Register(typeof(TEvent), new TransientEventHandlerFactory());
- }
-
- ///
- public IDisposable Register(Type eventType, IEventHandler handler)
- {
- return Register(eventType, new SingleInstanceHandlerFactory(handler));
- }
-
- ///
- public IDisposable Register(IEventHandlerFactory factory) where TEvent : class
- {
- return Register(typeof(TEvent), factory);
- }
-
- ///
- public IDisposable Register(Type eventType, IEventHandlerFactory factory)
- {
- GetOrCreateHandlerFactories(eventType)
- .Locking(factories => factories.Add(factory));
-
- return new EventHandlerFactoryUnregistrar(this, eventType, factory);
- }
-
- ///
- public void Unregister(Func action) where TEvent : class
- {
- Check.NotNull(action, nameof(action));
-
- GetOrCreateHandlerFactories(typeof(TEvent))
- .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 Unregister(IEventHandler handler) where TEvent : class
- {
- Unregister(typeof(TEvent), 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 TEvent : class
- {
- Unregister(typeof(TEvent), factory);
- }
-
- ///
- public void Unregister(Type eventType, IEventHandlerFactory factory)
- {
- GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory));
- }
-
- ///
- public void UnregisterAll() where TEvent : class
- {
- UnregisterAll(typeof(TEvent));
- }
-
- ///
- public void UnregisterAll(Type eventType)
- {
- GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear());
- }
-
- ///
- public Task TriggerAsync(TEvent eventData) where TEvent : class
- {
- return TriggerAsync(typeof(TEvent), 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)
- {
- await TriggerAsyncHandlingException(handlerFactory, handlerFactories.EventType, eventData, exceptions);
- }
- }
-
- //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 async Task TriggerAsyncHandlingException(IEventHandlerFactory asyncHandlerFactory, Type eventType, object eventData, List exceptions)
- {
- using (var eventHandlerWrapper = asyncHandlerFactory.GetHandler())
- {
- try
- {
- var asyncHandlerType = typeof(IEventHandler<>).MakeGenericType(eventType);
-
- var method = asyncHandlerType.GetMethod(
- "HandleEventAsync",
- new[] { eventType }
- );
-
- await (Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData });
- }
- catch (TargetInvocationException ex)
- {
- exceptions.Add(ex.InnerException);
- }
- catch (Exception ex)
- {
- exceptions.Add(ex);
- }
- }
- }
-
- 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/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs
new file mode 100644
index 0000000000..4c82dbd8eb
--- /dev/null
+++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs
@@ -0,0 +1,225 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Volo.Abp.EventBus.Distributed;
+using Volo.Abp.Reflection;
+
+namespace Volo.Abp.EventBus.Local
+{
+ public abstract class EventBusBase : IEventBus
+ {
+ ///
+ public virtual IDisposable Subscribe(Func action) where TEvent : class
+ {
+ return Subscribe(typeof(TEvent), new ActionEventHandler(action));
+ }
+
+ ///
+ public virtual IDisposable Subscribe(IEventHandler handler) where TEvent : class
+ {
+ return Subscribe(typeof(TEvent), handler);
+ }
+
+ ///
+ 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(IEventHandler 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)
+ {
+ var exceptions = new List();
+
+ await TriggerHandlersAsync(eventType, eventData, exceptions);
+
+ 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);
+ }
+ }
+
+ 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 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();
+
+ if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IEventHandler<>)))
+ {
+ var method = typeof(IEventHandler<>) //TODO: to a static field
+ .MakeGenericType(eventType)
+ .GetMethod(
+ nameof(IEventHandler