Browse Source

Merge branch 'eventbus'

pull/594/head
Halil ibrahim Kalkan 8 years ago
parent
commit
8addc78ab7
  1. 27
      framework/src/Volo.Abp.Core/System/Collections/Generic/AbpCollectionExtensions.cs
  2. 11
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs
  3. 241
      framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBus.cs
  4. 2
      framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBusOptions.cs
  5. 30
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/AbpEventBusModule.cs
  6. 2
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/ActionEventHandler.cs
  7. 64
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs
  8. 59
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs
  9. 14
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalEventBusOptions.cs
  10. 335
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBus.cs
  11. 225
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs
  12. 4
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerFactoryUnregistrar.cs
  13. 39
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventName.cs
  14. 126
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventBus.cs
  15. 25
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventPublisher.cs
  16. 111
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventSubscriber.cs
  17. 10
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/ILocalEventBus.cs
  18. 180
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs
  19. 6
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBusOptions.cs
  20. 90
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/NullEventBus.cs
  21. 91
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/NullLocalEventBus.cs
  22. 2
      framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/SingleInstanceHandlerFactory.cs
  23. 7
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
  24. 17
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/LocalDistributedEventBusTestBase.cs
  25. 21
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus_Test.cs
  26. 23
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/MySimpleDistributedTransientEventHandler.cs
  27. 48
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/InheritanceTest.cs
  28. 54
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/ActionBasedEventHandlerTest.cs
  29. 6
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBusTestBase.cs
  30. 10
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_DI_Services_Test.cs
  31. 12
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_Exception_Test.cs
  32. 8
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_MultipleHandle_Test.cs
  33. 10
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/GenericInheritanceTest.cs
  34. 48
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/InheritanceTest.cs
  35. 2
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/MySimpleEventDataHandler.cs
  36. 2
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/MySimpleTransientEventHandler.cs
  37. 10
      framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/TransientDisposableEventHandlerTest.cs
  38. 7
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs
  39. 13
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs

27
framework/src/Volo.Abp.Core/System/Collections/Generic/AbpCollectionExtensions.cs

@ -37,6 +37,33 @@ namespace System.Collections.Generic
return true; return true;
} }
/// <summary>
/// Adds items to the collection which are not already in the collection.
/// </summary>
/// <param name="source">The collection</param>
/// <param name="items">Item to check and add</param>
/// <typeparam name="T">Type of the items in the collection</typeparam>
/// <returns>Returns the added items.</returns>
public static IEnumerable<T> AddIfNotContains<T>([NotNull] this ICollection<T> source, IEnumerable<T> items)
{
Check.NotNull(source, nameof(source));
var addedItems = new List<T>();
foreach (var item in items)
{
if (source.Contains(item))
{
continue;
}
source.Add(item);
addedItems.Add(item);
}
return addedItems;
}
/// <summary> /// <summary>
/// Adds an item to the collection if it's not already in the collection based on the given <paramref name="predicate"/>. /// Adds an item to the collection if it's not already in the collection based on the given <paramref name="predicate"/>.
/// </summary> /// </summary>

11
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.DependencyInjection;
using Volo.Abp.DynamicProxy; using Volo.Abp.DynamicProxy;
using Volo.Abp.EventBus; using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Uow; using Volo.Abp.Uow;
namespace Volo.Abp.Domain.Entities.Events namespace Volo.Abp.Domain.Entities.Events
@ -14,14 +15,14 @@ namespace Volo.Abp.Domain.Entities.Events
/// </summary> /// </summary>
public class EntityChangeEventHelper : IEntityChangeEventHelper, ITransientDependency public class EntityChangeEventHelper : IEntityChangeEventHelper, ITransientDependency
{ {
public IEventBus EventBus { get; set; } public ILocalEventBus LocalEventBus { get; set; }
private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IUnitOfWorkManager _unitOfWorkManager;
public EntityChangeEventHelper(IUnitOfWorkManager unitOfWorkManager) public EntityChangeEventHelper(IUnitOfWorkManager unitOfWorkManager)
{ {
_unitOfWorkManager = unitOfWorkManager; _unitOfWorkManager = unitOfWorkManager;
EventBus = NullEventBus.Instance; LocalEventBus = NullLocalEventBus.Instance;
} }
public async Task TriggerEventsAsync(EntityChangeReport changeReport) public async Task TriggerEventsAsync(EntityChangeReport changeReport)
@ -115,7 +116,7 @@ namespace Volo.Abp.Domain.Entities.Events
{ {
foreach (var domainEvent in domainEvents) 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) if (triggerInCurrentUnitOfWork || _unitOfWorkManager.Current == null)
{ {
await EventBus.TriggerAsync(eventType, Activator.CreateInstance(eventType, entity)); await LocalEventBus.PublishAsync(eventType, Activator.CreateInstance(eventType, entity));
return; return;
} }
_unitOfWorkManager.Current.OnCompleted(() => EventBus.TriggerAsync(eventType, Activator.CreateInstance(eventType, entity))); _unitOfWorkManager.Current.OnCompleted(() => LocalEventBus.PublishAsync(eventType, Activator.CreateInstance(eventType, entity)));
} }
} }
} }

241
framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBus.cs

@ -1,83 +1,221 @@
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.RabbitMQ; using Volo.Abp.RabbitMQ;
using RabbitMQ.Client; 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 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: Implement Retry system
* TODO: Should be improved * 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 RabbitMqDistributedEventBusOptions RabbitMqDistributedEventBusOptions { get; }
protected IChannelPool ChannelPool { get; } protected DistributedEventBusOptions DistributedEventBusOptions { get; }
protected IConnectionPool ConnectionPool { get; }
protected IRabbitMqSerializer Serializer { get; } protected IRabbitMqSerializer Serializer { get; }
protected ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; } //TODO: Accessing to the List<IEventHandlerFactory> may not be thread-safe!
protected ConcurrentDictionary<string, Type> EventTypes { get; }
protected IModel ConsumerChannel;
protected IServiceProvider ServiceProvider { get; }
public RabbitMqDistributedEventBus( public RabbitMqDistributedEventBus(
IOptions<RabbitMqDistributedEventBusOptions> options, IOptions<RabbitMqDistributedEventBusOptions> options,
IChannelPool channelPool, IConnectionPool connectionPool,
IRabbitMqSerializer serializer) IRabbitMqSerializer serializer,
IServiceProvider serviceProvider,
DistributedEventBusOptions distributedEventBusOptions)
{ {
ChannelPool = channelPool; ConnectionPool = connectionPool;
Serializer = serializer; Serializer = serializer;
Options = options.Value; ServiceProvider = serviceProvider;
DistributedEventBusOptions = distributedEventBusOptions;
RabbitMqDistributedEventBusOptions = options.Value;
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
EventTypes = new ConcurrentDictionary<string, Type>();
ConsumerChannel = CreateConsumerChannel();
Subscribe(DistributedEventBusOptions.Handlers);
} }
public IDisposable Subscribe<TEvent>(Func<TEvent, Task> action) where TEvent : class public virtual void Subscribe(ITypeList<IEventHandler> 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<TEvent>(IEventHandler<TEvent> 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<TEvent, THandler>() 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<TEvent>(IEventHandlerFactory factory) where TEvent : class /// <inheritdoc/>
public override void Unsubscribe<TEvent>(Func<TEvent, Task> 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<TEvent>;
if (actionHandler == null)
{
return false;
}
return actionHandler.Action == action;
});
});
} }
public IDisposable Subscribe(Type eventType, IEventHandlerFactory factory) /// <inheritdoc/>
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>(TEvent eventData) /// <inheritdoc/>
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) /// <inheritdoc/>
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); 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? //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 properties.DeliveryMode = 2; //persistent
channelAccessor.Channel.BasicPublish( channel.BasicPublish(
exchange: Options.ExchangeName, exchange: RabbitMqDistributedEventBusOptions.ExchangeName,
routingKey: eventName, routingKey: eventName,
mandatory: true, mandatory: true,
basicProperties: properties, basicProperties: properties,
@ -87,5 +225,48 @@ namespace Volo.Abp.EventBus.Distributed.RabbitMq
return Task.CompletedTask; return Task.CompletedTask;
} }
private List<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType)
{
return HandlerFactories.GetOrAdd(
eventType,
type =>
{
var eventName = EventNameAttribute.GetName(type);
EventTypes[eventName] = type;
return new List<IEventHandlerFactory>();
}
);
}
protected override IEnumerable<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType)
{
var handlerFactoryList = new List<EventTypeWithEventHandlerFactories>();
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;
}
} }
} }

2
framework/src/Volo.Abp.EventBus.Distributed.RabbitMQ/Volo/Abp/EventBus/Distributed/RabbitMq/RabbitMqDistributedEventBusOptions.cs

@ -2,6 +2,8 @@
{ {
public class RabbitMqDistributedEventBusOptions public class RabbitMqDistributedEventBusOptions
{ {
public string ClientName { get; set; }
public string ExchangeName { get; set; } public string ExchangeName { get; set; }
} }
} }

30
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.Collections.Generic;
using System.Linq; using System.Linq;
using Microsoft.Extensions.DependencyInjection; using Volo.Abp.EventBus.Distributed;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.Reflection;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus
{ {
@ -15,22 +18,29 @@ namespace Volo.Abp.EventBus
private static void AddEventHandlers(IServiceCollection services) private static void AddEventHandlers(IServiceCollection services)
{ {
var handlers = new List<Type>(); var localHandlers = new List<Type>();
var distributedHandlers = new List<Type>();
services.OnRegistred(context => 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<EventBusOptions>(options => services.Configure<LocalEventBusOptions>(options =>
{ {
foreach (var handler in handlers) options.Handlers.AddIfNotContains(localHandlers);
{ });
options.Handlers.AddIfNotContains(handler);
} services.Configure<DistributedEventBusOptions>(options =>
{
options.Handlers.AddIfNotContains(distributedHandlers);
}); });
} }
} }

2
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 <see cref="IEventHandler{TEvent}"/> implementation. /// This event handler is an adapter to be able to use an action as <see cref="IEventHandler{TEvent}"/> implementation.
/// </summary> /// </summary>
/// <typeparam name="TEvent">Event type</typeparam> /// <typeparam name="TEvent">Event type</typeparam>
internal class ActionEventHandler<TEvent> : public class ActionEventHandler<TEvent> :
IEventHandler<TEvent>, IEventHandler<TEvent>,
ITransientDependency ITransientDependency
{ {

64
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/IDistributedEventBus.cs

@ -1,65 +1,7 @@
using System; namespace Volo.Abp.EventBus.Distributed
using System.Threading.Tasks;
namespace Volo.Abp.EventBus.Distributed
{ {
public interface IDistributedEventBus public interface IDistributedEventBus : IEventBus
{ {
/// <summary>
/// Subscribes to an event.
/// Given action is called for all event occurrences.
/// </summary>
/// <param name="action">Action to handle events</param>
/// <typeparam name="TEvent">Event type</typeparam>
IDisposable Subscribe<TEvent>(Func<TEvent, Task> action)
where TEvent : class;
/// <summary>
/// Subscribes to an event.
/// Same (given) instance of the handler is used for all event occurrences.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="handler">Object to handle the event</param>
IDisposable Subscribe<TEvent>(IEventHandler<TEvent> handler)
where TEvent : class;
/// <summary>
/// Subscribes to an event.
/// A new instance of <see cref="THandler"/> object is created for every event occurrence.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <typeparam name="THandler">Type of the event handler</typeparam>
IDisposable Subscribe<TEvent, THandler>()
where TEvent : class
where THandler : IEventHandler, new();
/// <summary>
/// Subscribes to an event.
/// Same (given) instance of the handler is used for all event occurrences.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="handler">Object to handle the event</param>
IDisposable Subscribe(Type eventType, IEventHandler handler);
/// <summary>
/// Subscribes to an event.
/// Given factory is used to create/release handlers
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="factory">A factory to create/release handlers</param>
IDisposable Subscribe<TEvent>(IEventHandlerFactory factory)
where TEvent : class;
/// <summary>
/// Subscribes to an event.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="factory">A factory to create/release handlers</param>
IDisposable Subscribe(Type eventType, IEventHandlerFactory factory);
Task PublishAsync<TEvent>(TEvent eventData)
where TEvent : class;
Task PublishAsync(Type eventType, object eventData);
} }
} }

59
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus.cs

@ -1,58 +1,95 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.EventBus.Local;
namespace Volo.Abp.EventBus.Distributed namespace Volo.Abp.EventBus.Distributed
{ {
[Dependency(TryRegister = true)] [Dependency(TryRegister = true)]
[ExposeServices(typeof(IDistributedEventBus), typeof(LocalDistributedEventBus))]
public class LocalDistributedEventBus : IDistributedEventBus, ITransientDependency 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<TEvent>(Func<TEvent, Task> action) where TEvent : class public IDisposable Subscribe<TEvent>(Func<TEvent, Task> action) where TEvent : class
{ {
return _eventBus.Register(action); return _localEventBus.Subscribe(action);
} }
public IDisposable Subscribe<TEvent>(IEventHandler<TEvent> handler) where TEvent : class public IDisposable Subscribe<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
{ {
return _eventBus.Register(handler); return _localEventBus.Subscribe(handler);
} }
public IDisposable Subscribe<TEvent, THandler>() where TEvent : class where THandler : IEventHandler, new() public IDisposable Subscribe<TEvent, THandler>() where TEvent : class where THandler : IEventHandler, new()
{ {
return _eventBus.Register<TEvent, THandler>(); return _localEventBus.Subscribe<TEvent, THandler>();
} }
public IDisposable Subscribe(Type eventType, IEventHandler handler) public IDisposable Subscribe(Type eventType, IEventHandler handler)
{ {
return _eventBus.Register(eventType, handler); return _localEventBus.Subscribe(eventType, handler);
} }
public IDisposable Subscribe<TEvent>(IEventHandlerFactory factory) where TEvent : class public IDisposable Subscribe<TEvent>(IEventHandlerFactory factory) where TEvent : class
{ {
return _eventBus.Register<TEvent>(factory); return _localEventBus.Subscribe<TEvent>(factory);
} }
public IDisposable Subscribe(Type eventType, IEventHandlerFactory factory) public IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
{ {
return _eventBus.Register(eventType, factory); return _localEventBus.Subscribe(eventType, factory);
}
public void Unsubscribe<TEvent>(Func<TEvent, Task> action) where TEvent : class
{
_localEventBus.Unsubscribe(action);
}
public void Unsubscribe<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
{
_localEventBus.Unsubscribe(handler);
}
public void Unsubscribe(Type eventType, IEventHandler handler)
{
_localEventBus.Unsubscribe(eventType, handler);
}
public void Unsubscribe<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
_localEventBus.Unsubscribe<TEvent>(factory);
}
public void Unsubscribe(Type eventType, IEventHandlerFactory factory)
{
_localEventBus.Unsubscribe(eventType, factory);
}
public void UnsubscribeAll<TEvent>() where TEvent : class
{
_localEventBus.UnsubscribeAll<TEvent>();
}
public void UnsubscribeAll(Type eventType)
{
_localEventBus.UnsubscribeAll(eventType);
} }
public Task PublishAsync<TEvent>(TEvent eventData) public Task PublishAsync<TEvent>(TEvent eventData)
where TEvent : class where TEvent : class
{ {
return _eventBus.TriggerAsync(eventData); return _localEventBus.PublishAsync(eventData);
} }
public Task PublishAsync(Type eventType, object eventData) public Task PublishAsync(Type eventType, object eventData)
{ {
return _eventBus.TriggerAsync(eventType, eventData); return _localEventBus.PublishAsync(eventType, eventData);
} }
} }
} }

14
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<IEventHandler> Handlers { get; }
public DistributedEventBusOptions()
{
Handlers = new TypeList<IEventHandler>();
}
}
}

335
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBus.cs

@ -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
{
/// <summary>
/// Implements EventBus as Singleton pattern.
/// </summary>
public class EventBus : IEventBus, ISingletonDependency
{
/// <summary>
/// Reference to the Logger.
/// </summary>
public ILogger<EventBus> Logger { get; set; }
/// <summary>
/// All registered handler factories.
/// Key: Type of the event
/// Value: List of handler factories
/// </summary>
protected ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; }
protected EventBusOptions Options { get; }
public EventBus(
IOptions<EventBusOptions> options,
IServiceProvider serviceProvider)
{
Options = options.Value;
Logger = NullLogger<EventBus>.Instance;
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
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));
}
}
}
}
/// <inheritdoc/>
public IDisposable Register<TEvent>(Func<TEvent, Task> action) where TEvent : class
{
return Register(typeof(TEvent), new ActionEventHandler<TEvent>(action));
}
/// <inheritdoc/>
public IDisposable Register<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
{
return Register(typeof(TEvent), handler);
}
/// <inheritdoc/>
public IDisposable Register<TEvent, THandler>()
where TEvent : class
where THandler : IEventHandler, new()
{
return Register(typeof(TEvent), new TransientEventHandlerFactory<THandler>());
}
/// <inheritdoc/>
public IDisposable Register(Type eventType, IEventHandler handler)
{
return Register(eventType, new SingleInstanceHandlerFactory(handler));
}
/// <inheritdoc/>
public IDisposable Register<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
return Register(typeof(TEvent), factory);
}
/// <inheritdoc/>
public IDisposable Register(Type eventType, IEventHandlerFactory factory)
{
GetOrCreateHandlerFactories(eventType)
.Locking(factories => factories.Add(factory));
return new EventHandlerFactoryUnregistrar(this, eventType, factory);
}
/// <inheritdoc/>
public void Unregister<TEvent>(Func<TEvent, Task> 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<TEvent>;
if (actionHandler == null)
{
return false;
}
return actionHandler.Action == action;
});
});
}
/// <inheritdoc/>
public void Unregister<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
{
Unregister(typeof(TEvent), handler);
}
/// <inheritdoc/>
public void Unregister(Type eventType, IEventHandler handler)
{
GetOrCreateHandlerFactories(eventType)
.Locking(factories =>
{
factories.RemoveAll(
factory =>
factory is SingleInstanceHandlerFactory &&
(factory as SingleInstanceHandlerFactory).HandlerInstance == handler
);
});
}
/// <inheritdoc/>
public void Unregister<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
Unregister(typeof(TEvent), factory);
}
/// <inheritdoc/>
public void Unregister(Type eventType, IEventHandlerFactory factory)
{
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory));
}
/// <inheritdoc/>
public void UnregisterAll<TEvent>() where TEvent : class
{
UnregisterAll(typeof(TEvent));
}
/// <inheritdoc/>
public void UnregisterAll(Type eventType)
{
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear());
}
/// <inheritdoc/>
public Task TriggerAsync<TEvent>(TEvent eventData) where TEvent : class
{
return TriggerAsync(typeof(TEvent), eventData);
}
/// <inheritdoc/>
public async Task TriggerAsync(Type eventType, object eventData)
{
var exceptions = new List<Exception>();
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<Exception> 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<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType)
{
var handlerFactoryList = new List<EventTypeWithEventHandlerFactories>();
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<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType)
{
return HandlerFactories.GetOrAdd(eventType, (type) => new List<IEventHandlerFactory>());
}
private class EventTypeWithEventHandlerFactories
{
public Type EventType { get; }
public List<IEventHandlerFactory> EventHandlerFactories { get; }
public EventTypeWithEventHandlerFactories(Type eventType, List<IEventHandlerFactory> eventHandlerFactories)
{
EventType = eventType;
EventHandlerFactories = eventHandlerFactories;
}
}
// Reference from
// https://blogs.msdn.microsoft.com/benwilli/2017/02/09/an-alternative-to-configureawaitfalse-everywhere/
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()
{
}
}
}
}

225
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
{
/// <inheritdoc/>
public virtual IDisposable Subscribe<TEvent>(Func<TEvent, Task> action) where TEvent : class
{
return Subscribe(typeof(TEvent), new ActionEventHandler<TEvent>(action));
}
/// <inheritdoc/>
public virtual IDisposable Subscribe<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
{
return Subscribe(typeof(TEvent), handler);
}
/// <inheritdoc/>
public virtual IDisposable Subscribe<TEvent, THandler>()
where TEvent : class
where THandler : IEventHandler, new()
{
return Subscribe(typeof(TEvent), new TransientEventHandlerFactory<THandler>());
}
/// <inheritdoc/>
public virtual IDisposable Subscribe(Type eventType, IEventHandler handler)
{
return Subscribe(eventType, new SingleInstanceHandlerFactory(handler));
}
/// <inheritdoc/>
public virtual IDisposable Subscribe<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
return Subscribe(typeof(TEvent), factory);
}
public abstract IDisposable Subscribe(Type eventType, IEventHandlerFactory factory);
public abstract void Unsubscribe<TEvent>(Func<TEvent, Task> action) where TEvent : class;
/// <inheritdoc/>
public virtual void Unsubscribe<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
{
Unsubscribe(typeof(TEvent), handler);
}
public abstract void Unsubscribe(Type eventType, IEventHandler handler);
/// <inheritdoc/>
public virtual void Unsubscribe<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
Unsubscribe(typeof(TEvent), factory);
}
public abstract void Unsubscribe(Type eventType, IEventHandlerFactory factory);
/// <inheritdoc/>
public virtual void UnsubscribeAll<TEvent>() where TEvent : class
{
UnsubscribeAll(typeof(TEvent));
}
/// <inheritdoc/>
public abstract void UnsubscribeAll(Type eventType);
/// <inheritdoc/>
public virtual Task PublishAsync<TEvent>(TEvent eventData) where TEvent : class
{
return PublishAsync(typeof(TEvent), eventData);
}
/// <inheritdoc/>
public abstract Task PublishAsync(Type eventType, object eventData);
public virtual async Task TriggerHandlersAsync(Type eventType, object eventData)
{
var exceptions = new List<Exception>();
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<Exception> exceptions)
{
await new SynchronizationContextRemover();
foreach (var handlerFactories in GetHandlerFactories(eventType))
{
foreach (var handlerFactory in handlerFactories.EventHandlerFactories)
{
await TriggerHandlerAsync(handlerFactory, handlerFactories.EventType, eventData, exceptions);
}
}
//Implements generic argument inheritance. See IEventDataWithInheritableGenericArgument
if (eventType.GetTypeInfo().IsGenericType &&
eventType.GetGenericArguments().Length == 1 &&
typeof(IEventDataWithInheritableGenericArgument).IsAssignableFrom(eventType))
{
var genericArg = eventType.GetGenericArguments()[0];
var baseArg = genericArg.GetTypeInfo().BaseType;
if (baseArg != null)
{
var baseEventType = eventType.GetGenericTypeDefinition().MakeGenericType(baseArg);
var constructorArgs = ((IEventDataWithInheritableGenericArgument)eventData).GetConstructorArgs();
var baseEventData = Activator.CreateInstance(baseEventType, constructorArgs);
await PublishAsync(baseEventType, baseEventData);
}
}
}
protected abstract IEnumerable<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType);
protected virtual async Task TriggerHandlerAsync(IEventHandlerFactory asyncHandlerFactory, Type eventType, object eventData, List<Exception> exceptions)
{
using (var eventHandlerWrapper = asyncHandlerFactory.GetHandler())
{
try
{
var handlerType = eventHandlerWrapper.EventHandler.GetType();
if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IEventHandler<>)))
{
var method = typeof(IEventHandler<>) //TODO: to a static field
.MakeGenericType(eventType)
.GetMethod(
nameof(IEventHandler<object>.HandleEventAsync),
new[] { eventType }
);
await (Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData });
}
else if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IDistributedEventHandler<>)))
{
var method = typeof(IDistributedEventHandler<>) //TODO: to a static field
.MakeGenericType(eventType)
.GetMethod(
nameof(IDistributedEventHandler<object>.HandleEventAsync),
new[] { eventType }
);
await (Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData });
}
else
{
throw new AbpException("The object instance is not an event handler. Object type: " + handlerType.AssemblyQualifiedName);
}
}
catch (TargetInvocationException ex)
{
exceptions.Add(ex.InnerException);
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
}
protected class EventTypeWithEventHandlerFactories
{
public Type EventType { get; }
public List<IEventHandlerFactory> EventHandlerFactories { get; }
public EventTypeWithEventHandlerFactories(Type eventType, List<IEventHandlerFactory> eventHandlerFactories)
{
EventType = eventType;
EventHandlerFactories = eventHandlerFactories;
}
}
// Reference from
// https://blogs.msdn.microsoft.com/benwilli/2017/02/09/an-alternative-to-configureawaitfalse-everywhere/
protected struct SynchronizationContextRemover : INotifyCompletion
{
public bool IsCompleted
{
get { return SynchronizationContext.Current == null; }
}
public void OnCompleted(Action continuation)
{
var prevContext = SynchronizationContext.Current;
try
{
SynchronizationContext.SetSynchronizationContext(null);
continuation();
}
finally
{
SynchronizationContext.SetSynchronizationContext(prevContext);
}
}
public SynchronizationContextRemover GetAwaiter()
{
return this;
}
public void GetResult()
{
}
}
}
}

4
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerFactoryUnregistrar.cs

@ -5,7 +5,7 @@ namespace Volo.Abp.EventBus
/// <summary> /// <summary>
/// Used to unregister a <see cref="IEventHandlerFactory"/> on <see cref="Dispose"/> method. /// Used to unregister a <see cref="IEventHandlerFactory"/> on <see cref="Dispose"/> method.
/// </summary> /// </summary>
internal class EventHandlerFactoryUnregistrar : IDisposable public class EventHandlerFactoryUnregistrar : IDisposable
{ {
private readonly IEventBus _eventBus; private readonly IEventBus _eventBus;
private readonly Type _eventType; private readonly Type _eventType;
@ -20,7 +20,7 @@ namespace Volo.Abp.EventBus
public void Dispose() public void Dispose()
{ {
_eventBus.Unregister(_eventType, _factory); _eventBus.Unsubscribe(_eventType, _factory);
} }
} }
} }

39
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventName.cs

@ -0,0 +1,39 @@
using System;
using System.Linq;
using JetBrains.Annotations;
namespace Volo.Abp.EventBus
{
[AttributeUsage(AttributeTargets.Class)]
public class EventNameAttribute : Attribute, IEventNameProvider
{
public string Name { get; }
public EventNameAttribute([NotNull] string name)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name));
}
public static string GetName<TEvent>()
{
return GetName(typeof(TEvent));
}
public static string GetName([NotNull] Type eventType)
{
Check.NotNull(eventType, nameof(eventType));
return eventType
.GetCustomAttributes(true)
.OfType<IEventNameProvider>()
.FirstOrDefault()
?.Name
?? eventType.FullName;
}
}
public interface IEventNameProvider
{
string Name { get; }
}
}

126
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventBus.cs

@ -1,131 +1,7 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus
{ {
/// <summary> public interface IEventBus : IEventSubscriber, IEventPublisher
/// Defines interface of the event bus.
/// </summary>
public interface IEventBus
{ {
/// <summary>
/// Registers to an event.
/// Given action is called for all event occurrences.
/// </summary>
/// <param name="action">Action to handle events</param>
/// <typeparam name="TEvent">Event type</typeparam>
IDisposable Register<TEvent>(Func<TEvent, Task> action)
where TEvent : class;
/// <summary>
/// Registers to an event.
/// Same (given) instance of the handler is used for all event occurrences.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="handler">Object to handle the event</param>
IDisposable Register<TEvent>(IEventHandler<TEvent> handler)
where TEvent : class;
/// <summary>
/// Registers to an event.
/// A new instance of <see cref="THandler"/> object is created for every event occurrence.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <typeparam name="THandler">Type of the event handler</typeparam>
IDisposable Register<TEvent, THandler>()
where TEvent : class
where THandler : IEventHandler, new();
/// <summary>
/// Registers to an event.
/// Same (given) instance of the handler is used for all event occurrences.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="handler">Object to handle the event</param>
IDisposable Register(Type eventType, IEventHandler handler);
/// <summary>
/// Registers to an event.
/// Given factory is used to create/release handlers
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="factory">A factory to create/release handlers</param>
IDisposable Register<TEvent>(IEventHandlerFactory factory)
where TEvent : class;
/// <summary>
/// Registers to an event.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="factory">A factory to create/release handlers</param>
IDisposable Register(Type eventType, IEventHandlerFactory factory);
/// <summary>
/// Unregisters from an event.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="action"></param>
void Unregister<TEvent>(Func<TEvent, Task> action)
where TEvent : class;
/// <summary>
/// Unregisters from an event.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="handler">Handler object that is registered before</param>
void Unregister<TEvent>(IEventHandler<TEvent> handler)
where TEvent : class;
/// <summary>
/// Unregisters from an event.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="handler">Handler object that is registered before</param>
void Unregister(Type eventType, IEventHandler handler);
/// <summary>
/// Unregisters from an event.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="factory">Factory object that is registered before</param>
void Unregister<TEvent>(IEventHandlerFactory factory)
where TEvent : class;
/// <summary>
/// Unregisters from an event.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="factory">Factory object that is registered before</param>
void Unregister(Type eventType, IEventHandlerFactory factory);
/// <summary>
/// Unregisters all event handlers of given event type.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
void UnregisterAll<TEvent>()
where TEvent : class;
/// <summary>
/// Unregisters all event handlers of given event type.
/// </summary>
/// <param name="eventType">Event type</param>
void UnregisterAll(Type eventType);
/// <summary>
/// Triggers an event asynchronously.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="eventData">Related data for the event</param>
/// <returns>The task to handle async operation</returns>
Task TriggerAsync<TEvent>(TEvent eventData)
where TEvent : class;
/// <summary>
/// Triggers an event asynchronously.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="eventData">Related data for the event</param>
/// <returns>The task to handle async operation</returns>
Task TriggerAsync(Type eventType, object eventData);
} }
} }

25
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventPublisher.cs

@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.EventBus
{
public interface IEventPublisher
{
/// <summary>
/// Triggers an event asynchronously.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="eventData">Related data for the event</param>
/// <returns>The task to handle async operation</returns>
Task PublishAsync<TEvent>(TEvent eventData)
where TEvent : class;
/// <summary>
/// Triggers an event asynchronously.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="eventData">Related data for the event</param>
/// <returns>The task to handle async operation</returns>
Task PublishAsync(Type eventType, object eventData);
}
}

111
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventSubscriber.cs

@ -0,0 +1,111 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.EventBus
{
public interface IEventSubscriber
{
/// <summary>
/// Registers to an event.
/// Given action is called for all event occurrences.
/// </summary>
/// <param name="action">Action to handle events</param>
/// <typeparam name="TEvent">Event type</typeparam>
IDisposable Subscribe<TEvent>(Func<TEvent, Task> action)
where TEvent : class;
/// <summary>
/// Registers to an event.
/// Same (given) instance of the handler is used for all event occurrences.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="handler">Object to handle the event</param>
IDisposable Subscribe<TEvent>(IEventHandler<TEvent> handler)
where TEvent : class;
/// <summary>
/// Registers to an event.
/// A new instance of <see cref="THandler"/> object is created for every event occurrence.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <typeparam name="THandler">Type of the event handler</typeparam>
IDisposable Subscribe<TEvent, THandler>()
where TEvent : class
where THandler : IEventHandler, new();
/// <summary>
/// Registers to an event.
/// Same (given) instance of the handler is used for all event occurrences.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="handler">Object to handle the event</param>
IDisposable Subscribe(Type eventType, IEventHandler handler);
/// <summary>
/// Registers to an event.
/// Given factory is used to create/release handlers
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="factory">A factory to create/release handlers</param>
IDisposable Subscribe<TEvent>(IEventHandlerFactory factory)
where TEvent : class;
/// <summary>
/// Registers to an event.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="factory">A factory to create/release handlers</param>
IDisposable Subscribe(Type eventType, IEventHandlerFactory factory);
/// <summary>
/// Unregisters from an event.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="action"></param>
void Unsubscribe<TEvent>(Func<TEvent, Task> action)
where TEvent : class;
/// <summary>
/// Unregisters from an event.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="handler">Handler object that is registered before</param>
void Unsubscribe<TEvent>(IEventHandler<TEvent> handler)
where TEvent : class;
/// <summary>
/// Unregisters from an event.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="handler">Handler object that is registered before</param>
void Unsubscribe(Type eventType, IEventHandler handler);
/// <summary>
/// Unregisters from an event.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
/// <param name="factory">Factory object that is registered before</param>
void Unsubscribe<TEvent>(IEventHandlerFactory factory)
where TEvent : class;
/// <summary>
/// Unregisters from an event.
/// </summary>
/// <param name="eventType">Event type</param>
/// <param name="factory">Factory object that is registered before</param>
void Unsubscribe(Type eventType, IEventHandlerFactory factory);
/// <summary>
/// Unregisters all event handlers of given event type.
/// </summary>
/// <typeparam name="TEvent">Event type</typeparam>
void UnsubscribeAll<TEvent>()
where TEvent : class;
/// <summary>
/// Unregisters all event handlers of given event type.
/// </summary>
/// <param name="eventType">Event type</param>
void UnsubscribeAll(Type eventType);
}
}

10
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/ILocalEventBus.cs

@ -0,0 +1,10 @@
namespace Volo.Abp.EventBus.Local
{
/// <summary>
/// Defines interface of the event bus.
/// </summary>
public interface ILocalEventBus : IEventBus
{
}
}

180
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs

@ -0,0 +1,180 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Volo.Abp.Collections;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Threading;
namespace Volo.Abp.EventBus.Local
{
/// <summary>
/// Implements EventBus as Singleton pattern.
/// </summary>
[ExposeServices(typeof(ILocalEventBus), typeof(LocalEventBus))]
public class LocalEventBus : EventBusBase, ILocalEventBus, ISingletonDependency
{
/// <summary>
/// Reference to the Logger.
/// </summary>
public ILogger<LocalEventBus> Logger { get; set; }
protected LocalEventBusOptions Options { get; }
protected ConcurrentDictionary<Type, List<IEventHandlerFactory>> HandlerFactories { get; }
protected IServiceProvider ServiceProvider { get; }
public LocalEventBus(
IOptions<LocalEventBusOptions> options,
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
Options = options.Value;
Logger = NullLogger<LocalEventBus>.Instance;
HandlerFactories = new ConcurrentDictionary<Type, List<IEventHandlerFactory>>();
Subscribe(Options.Handlers);
}
public virtual void Subscribe(ITypeList<IEventHandler> handlers)
{
foreach (var handler in handlers)
{
var interfaces = handler.GetInterfaces();
foreach (var @interface in interfaces)
{
if (!typeof(IEventHandler).GetTypeInfo().IsAssignableFrom(@interface))
{
continue;
}
var genericArgs = @interface.GetGenericArguments();
if (genericArgs.Length == 1)
{
Subscribe(genericArgs[0], new IocEventHandlerFactory(ServiceProvider, handler));
}
}
}
}
/// <inheritdoc/>
public override IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
{
GetOrCreateHandlerFactories(eventType)
.Locking(factories => factories.Add(factory));
return new EventHandlerFactoryUnregistrar(this, eventType, factory);
}
/// <inheritdoc/>
public override void Unsubscribe<TEvent>(Func<TEvent, Task> action)
{
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<TEvent>;
if (actionHandler == null)
{
return false;
}
return actionHandler.Action == action;
});
});
}
/// <inheritdoc/>
public override void Unsubscribe(Type eventType, IEventHandler handler)
{
GetOrCreateHandlerFactories(eventType)
.Locking(factories =>
{
factories.RemoveAll(
factory =>
factory is SingleInstanceHandlerFactory &&
(factory as SingleInstanceHandlerFactory).HandlerInstance == handler
);
});
}
/// <inheritdoc/>
public override void Unsubscribe(Type eventType, IEventHandlerFactory factory)
{
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Remove(factory));
}
/// <inheritdoc/>
public override void UnsubscribeAll(Type eventType)
{
GetOrCreateHandlerFactories(eventType).Locking(factories => factories.Clear());
}
public override async Task PublishAsync(Type eventType, object eventData)
{
var exceptions = new List<Exception>();
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 override IEnumerable<EventTypeWithEventHandlerFactories> GetHandlerFactories(Type eventType)
{
var handlerFactoryList = new List<EventTypeWithEventHandlerFactories>();
foreach (var handlerFactory in HandlerFactories.Where(hf => ShouldTriggerEventForHandler(eventType, hf.Key)))
{
handlerFactoryList.Add(new EventTypeWithEventHandlerFactories(handlerFactory.Key, handlerFactory.Value));
}
return handlerFactoryList.ToArray();
}
private List<IEventHandlerFactory> GetOrCreateHandlerFactories(Type eventType)
{
return HandlerFactories.GetOrAdd(eventType, (type) => new List<IEventHandlerFactory>());
}
private static bool ShouldTriggerEventForHandler(Type targetEventType, Type handlerEventType)
{
//Should trigger same type
if (handlerEventType == targetEventType)
{
return true;
}
//Should trigger for inherited types
if (handlerEventType.IsAssignableFrom(targetEventType))
{
return true;
}
return false;
}
}
}

6
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusOptions.cs → framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBusOptions.cs

@ -1,12 +1,12 @@
using Volo.Abp.Collections; using Volo.Abp.Collections;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus.Local
{ {
public class EventBusOptions public class LocalEventBusOptions
{ {
public ITypeList<IEventHandler> Handlers { get; } public ITypeList<IEventHandler> Handlers { get; }
public EventBusOptions() public LocalEventBusOptions()
{ {
Handlers = new TypeList<IEventHandler>(); Handlers = new TypeList<IEventHandler>();
} }

90
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/NullEventBus.cs

@ -1,90 +0,0 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.EventBus
{
public sealed class NullEventBus : IEventBus
{
public static NullEventBus Instance { get; } = new NullEventBus();
private NullEventBus()
{
}
public IDisposable Register<TEvent>(Func<TEvent, Task> action) where TEvent : class
{
return NullDisposable.Instance;
}
public IDisposable Register<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
{
return NullDisposable.Instance;
}
public IDisposable Register<TEvent, THandler>() where TEvent : class where THandler : IEventHandler, new()
{
return NullDisposable.Instance;
}
public IDisposable Register(Type eventType, IEventHandler handler)
{
return NullDisposable.Instance;
}
public IDisposable Register<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
return NullDisposable.Instance;
}
public IDisposable Register(Type eventType, IEventHandlerFactory factory)
{
return NullDisposable.Instance;
}
public void Unregister<TEvent>(Func<TEvent, Task> action) where TEvent : class
{
}
public void Unregister<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
{
}
public void Unregister(Type eventType, IEventHandler handler)
{
}
public void Unregister<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
}
public void Unregister(Type eventType, IEventHandlerFactory factory)
{
}
public void UnregisterAll<TEvent>() where TEvent : class
{
}
public void UnregisterAll(Type eventType)
{
}
public Task TriggerAsync<TEvent>(TEvent eventData) where TEvent : class
{
return Task.CompletedTask;
}
public Task TriggerAsync(Type eventType, object eventData)
{
return Task.CompletedTask;
}
}
}

91
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/NullLocalEventBus.cs

@ -0,0 +1,91 @@
using System;
using System.Threading.Tasks;
using Volo.Abp.EventBus.Local;
namespace Volo.Abp.EventBus
{
public sealed class NullLocalEventBus : ILocalEventBus
{
public static NullLocalEventBus Instance { get; } = new NullLocalEventBus();
private NullLocalEventBus()
{
}
public IDisposable Subscribe<TEvent>(Func<TEvent, Task> action) where TEvent : class
{
return NullDisposable.Instance;
}
public IDisposable Subscribe<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
{
return NullDisposable.Instance;
}
public IDisposable Subscribe<TEvent, THandler>() where TEvent : class where THandler : IEventHandler, new()
{
return NullDisposable.Instance;
}
public IDisposable Subscribe(Type eventType, IEventHandler handler)
{
return NullDisposable.Instance;
}
public IDisposable Subscribe<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
return NullDisposable.Instance;
}
public IDisposable Subscribe(Type eventType, IEventHandlerFactory factory)
{
return NullDisposable.Instance;
}
public void Unsubscribe<TEvent>(Func<TEvent, Task> action) where TEvent : class
{
}
public void Unsubscribe<TEvent>(IEventHandler<TEvent> handler) where TEvent : class
{
}
public void Unsubscribe(Type eventType, IEventHandler handler)
{
}
public void Unsubscribe<TEvent>(IEventHandlerFactory factory) where TEvent : class
{
}
public void Unsubscribe(Type eventType, IEventHandlerFactory factory)
{
}
public void UnsubscribeAll<TEvent>() where TEvent : class
{
}
public void UnsubscribeAll(Type eventType)
{
}
public Task PublishAsync<TEvent>(TEvent eventData) where TEvent : class
{
return Task.CompletedTask;
}
public Task PublishAsync(Type eventType, object eventData)
{
return Task.CompletedTask;
}
}
}

2
framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/SingleInstanceHandlerFactory.cs

@ -7,7 +7,7 @@ namespace Volo.Abp.EventBus
/// <remarks> /// <remarks>
/// This class always gets the same single instance of handler. /// This class always gets the same single instance of handler.
/// </remarks> /// </remarks>
internal class SingleInstanceHandlerFactory : IEventHandlerFactory public class SingleInstanceHandlerFactory : IEventHandlerFactory
{ {
/// <summary> /// <summary>
/// The event handler instance. /// The event handler instance.

7
framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs

@ -10,6 +10,7 @@ using Volo.Abp.Auditing;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.EventBus; using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.MongoDB; using Volo.Abp.MongoDB;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
@ -31,7 +32,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
protected IMongoDbContextProvider<TMongoDbContext> DbContextProvider { get; } protected IMongoDbContextProvider<TMongoDbContext> DbContextProvider { get; }
public IEventBus EventBus { get; set; } public ILocalEventBus LocalEventBus { get; set; }
public IEntityChangeEventHelper EntityChangeEventHelper { get; set; } public IEntityChangeEventHelper EntityChangeEventHelper { get; set; }
@ -43,7 +44,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
{ {
DbContextProvider = dbContextProvider; DbContextProvider = dbContextProvider;
EventBus = NullEventBus.Instance; LocalEventBus = NullLocalEventBus.Instance;
EntityChangeEventHelper = NullEntityChangeEventHelper.Instance; EntityChangeEventHelper = NullEntityChangeEventHelper.Instance;
} }
@ -313,7 +314,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
foreach (var entityEvent in entityEvents) foreach (var entityEvent in entityEvents)
{ {
await EventBus.TriggerAsync(entityEvent.GetType(), entityEvent); await LocalEventBus.PublishAsync(entityEvent.GetType(), entityEvent);
} }
generatesDomainEventsEntity.ClearDomainEvents(); generatesDomainEventsEntity.ClearDomainEvents();

17
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/LocalDistributedEventBusTestBase.cs

@ -0,0 +1,17 @@
namespace Volo.Abp.EventBus.Distributed
{
public abstract class LocalDistributedEventBusTestBase : AbpIntegratedTest<EventBusTestModule>
{
protected LocalDistributedEventBus LocalEventBus;
protected LocalDistributedEventBusTestBase()
{
LocalEventBus = GetRequiredService<LocalDistributedEventBus>();
}
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
}
}

21
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/LocalDistributedEventBus_Test.cs

@ -0,0 +1,21 @@
using System.Threading.Tasks;
using Xunit;
namespace Volo.Abp.EventBus.Distributed
{
public class LocalDistributedEventBus_Test : LocalDistributedEventBusTestBase
{
[Fact]
public async Task Should_Call_Handler_AndDispose()
{
LocalEventBus.Subscribe<MySimpleEventData, MySimpleDistributedTransientEventHandler>();
await LocalEventBus.PublishAsync(new MySimpleEventData(1));
await LocalEventBus.PublishAsync(new MySimpleEventData(2));
await LocalEventBus.PublishAsync(new MySimpleEventData(3));
Assert.Equal(3, MySimpleDistributedTransientEventHandler.HandleCount);
Assert.Equal(3, MySimpleDistributedTransientEventHandler.DisposeCount);
}
}
}

23
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Distributed/MySimpleDistributedTransientEventHandler.cs

@ -0,0 +1,23 @@
using System;
using System.Threading.Tasks;
namespace Volo.Abp.EventBus.Distributed
{
public class MySimpleDistributedTransientEventHandler : IDistributedEventHandler<MySimpleEventData>, IDisposable
{
public static int HandleCount { get; set; }
public static int DisposeCount { get; set; }
public Task HandleEventAsync(MySimpleEventData eventData)
{
++HandleCount;
return Task.CompletedTask;
}
public void Dispose()
{
++DisposeCount;
}
}
}

48
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/InheritanceTest.cs

@ -1,48 +0,0 @@
using System.Threading.Tasks;
using Xunit;
namespace Volo.Abp.EventBus
{
public class InheritanceTest : EventBusTestBase
{
[Fact]
public async Task Should_Handle_Events_For_Derived_Classes()
{
var totalData = 0;
EventBus.Register<MySimpleEventData>(
eventData =>
{
totalData += eventData.Value;
return Task.CompletedTask;
});
await EventBus.TriggerAsync(new MySimpleEventData(1)); //Should handle directly registered class
await EventBus.TriggerAsync(new MySimpleEventData(2)); //Should handle directly registered class
await EventBus.TriggerAsync(new MyDerivedEventData(3)); //Should handle derived class too
await EventBus.TriggerAsync(new MyDerivedEventData(4)); //Should handle derived class too
Assert.Equal(10, totalData);
}
[Fact]
public async Task Should_Not_Handle_Events_For_Base_Classes()
{
var totalData = 0;
EventBus.Register<MyDerivedEventData>(
eventData =>
{
totalData += eventData.Value;
return Task.CompletedTask;
});
await EventBus.TriggerAsync(new MySimpleEventData(1)); //Should not handle
await EventBus.TriggerAsync(new MySimpleEventData(2)); //Should not handle
await EventBus.TriggerAsync(new MyDerivedEventData(3)); //Should handle
await EventBus.TriggerAsync(new MyDerivedEventData(4)); //Should handle
Assert.Equal(7, totalData);
}
}
}

54
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/ActionBasedEventHandlerTest.cs → framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/ActionBasedEventHandlerTest.cs

@ -3,7 +3,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Xunit;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus.Local
{ {
public class ActionBasedEventHandlerTest : EventBusTestBase public class ActionBasedEventHandlerTest : EventBusTestBase
{ {
@ -12,17 +12,17 @@ namespace Volo.Abp.EventBus
{ {
var totalData = 0; var totalData = 0;
EventBus.Register<MySimpleEventData>( LocalEventBus.Subscribe<MySimpleEventData>(
eventData => eventData =>
{ {
totalData += eventData.Value; totalData += eventData.Value;
return Task.CompletedTask; return Task.CompletedTask;
}); });
await EventBus.TriggerAsync(new MySimpleEventData(1)); await LocalEventBus.PublishAsync(new MySimpleEventData(1));
await EventBus.TriggerAsync(new MySimpleEventData(2)); await LocalEventBus.PublishAsync(new MySimpleEventData(2));
await EventBus.TriggerAsync(new MySimpleEventData(3)); await LocalEventBus.PublishAsync(new MySimpleEventData(3));
await EventBus.TriggerAsync(new MySimpleEventData(4)); await LocalEventBus.PublishAsync(new MySimpleEventData(4));
Assert.Equal(10, totalData); Assert.Equal(10, totalData);
} }
@ -32,17 +32,17 @@ namespace Volo.Abp.EventBus
{ {
var totalData = 0; var totalData = 0;
EventBus.Register<MySimpleEventData>( LocalEventBus.Subscribe<MySimpleEventData>(
eventData => eventData =>
{ {
totalData += eventData.Value; totalData += eventData.Value;
return Task.CompletedTask; return Task.CompletedTask;
}); });
await EventBus.TriggerAsync(typeof(MySimpleEventData), new MySimpleEventData(1)); await LocalEventBus.PublishAsync(typeof(MySimpleEventData), new MySimpleEventData(1));
await EventBus.TriggerAsync(typeof(MySimpleEventData), new MySimpleEventData(2)); await LocalEventBus.PublishAsync(typeof(MySimpleEventData), new MySimpleEventData(2));
await EventBus.TriggerAsync(typeof(MySimpleEventData), new MySimpleEventData(3)); await LocalEventBus.PublishAsync(typeof(MySimpleEventData), new MySimpleEventData(3));
await EventBus.TriggerAsync(typeof(MySimpleEventData), new MySimpleEventData(4)); await LocalEventBus.PublishAsync(typeof(MySimpleEventData), new MySimpleEventData(4));
Assert.Equal(10, totalData); Assert.Equal(10, totalData);
} }
@ -52,20 +52,20 @@ namespace Volo.Abp.EventBus
{ {
var totalData = 0; var totalData = 0;
var registerDisposer = EventBus.Register<MySimpleEventData>( var registerDisposer = LocalEventBus.Subscribe<MySimpleEventData>(
eventData => eventData =>
{ {
totalData += eventData.Value; totalData += eventData.Value;
return Task.CompletedTask; return Task.CompletedTask;
}); });
await EventBus.TriggerAsync(new MySimpleEventData(1)); await LocalEventBus.PublishAsync(new MySimpleEventData(1));
await EventBus.TriggerAsync(new MySimpleEventData(2)); await LocalEventBus.PublishAsync(new MySimpleEventData(2));
await EventBus.TriggerAsync(new MySimpleEventData(3)); await LocalEventBus.PublishAsync(new MySimpleEventData(3));
registerDisposer.Dispose(); registerDisposer.Dispose();
await EventBus.TriggerAsync(new MySimpleEventData(4)); await LocalEventBus.PublishAsync(new MySimpleEventData(4));
Assert.Equal(6, totalData); Assert.Equal(6, totalData);
} }
@ -82,15 +82,15 @@ namespace Volo.Abp.EventBus
return Task.CompletedTask; return Task.CompletedTask;
}); });
EventBus.Register(action); LocalEventBus.Subscribe(action);
await EventBus.TriggerAsync(new MySimpleEventData(1)); await LocalEventBus.PublishAsync(new MySimpleEventData(1));
await EventBus.TriggerAsync(new MySimpleEventData(2)); await LocalEventBus.PublishAsync(new MySimpleEventData(2));
await EventBus.TriggerAsync(new MySimpleEventData(3)); await LocalEventBus.PublishAsync(new MySimpleEventData(3));
EventBus.Unregister(action); LocalEventBus.Unsubscribe(action);
await EventBus.TriggerAsync(new MySimpleEventData(4)); await LocalEventBus.PublishAsync(new MySimpleEventData(4));
Assert.Equal(6, totalData); Assert.Equal(6, totalData);
} }
@ -100,7 +100,7 @@ namespace Volo.Abp.EventBus
{ {
int totalData = 0; int totalData = 0;
EventBus.Register<MySimpleEventData>( LocalEventBus.Subscribe<MySimpleEventData>(
async eventData => async eventData =>
{ {
await Task.Delay(20); await Task.Delay(20);
@ -108,10 +108,10 @@ namespace Volo.Abp.EventBus
await Task.Delay(20); await Task.Delay(20);
}); });
await EventBus.TriggerAsync(new MySimpleEventData(1)); await LocalEventBus.PublishAsync(new MySimpleEventData(1));
await EventBus.TriggerAsync(new MySimpleEventData(2)); await LocalEventBus.PublishAsync(new MySimpleEventData(2));
await EventBus.TriggerAsync(new MySimpleEventData(3)); await LocalEventBus.PublishAsync(new MySimpleEventData(3));
await EventBus.TriggerAsync(new MySimpleEventData(4)); await LocalEventBus.PublishAsync(new MySimpleEventData(4));
Assert.Equal(10, totalData); Assert.Equal(10, totalData);
} }

6
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBusTestBase.cs → framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBusTestBase.cs

@ -1,12 +1,12 @@
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus.Local
{ {
public abstract class EventBusTestBase : AbpIntegratedTest<EventBusTestModule> public abstract class EventBusTestBase : AbpIntegratedTest<EventBusTestModule>
{ {
protected IEventBus EventBus; protected ILocalEventBus LocalEventBus;
protected EventBusTestBase() protected EventBusTestBase()
{ {
EventBus = GetRequiredService<IEventBus>(); LocalEventBus = GetRequiredService<ILocalEventBus>();
} }
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)

10
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBus_DI_Services_Test.cs → framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_DI_Services_Test.cs

@ -2,17 +2,17 @@
using Shouldly; using Shouldly;
using Xunit; using Xunit;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus.Local
{ {
public class EventBus_DI_Services_Test : EventBusTestBase public class EventBus_DI_Services_Test : EventBusTestBase
{ {
[Fact] [Fact]
public async Task Should_Automatically_Register_EventHandlers_From_Services() public async Task Should_Automatically_Register_EventHandlers_From_Services()
{ {
await EventBus.TriggerAsync(new MySimpleEventData(1)); await LocalEventBus.PublishAsync(new MySimpleEventData(1));
await EventBus.TriggerAsync(new MySimpleEventData(2)); await LocalEventBus.PublishAsync(new MySimpleEventData(2));
await EventBus.TriggerAsync(new MySimpleEventData(3)); await LocalEventBus.PublishAsync(new MySimpleEventData(3));
await EventBus.TriggerAsync(new MySimpleEventData(4)); await LocalEventBus.PublishAsync(new MySimpleEventData(4));
GetRequiredService<MySimpleEventDataHandler>().TotalData.ShouldBe(10); GetRequiredService<MySimpleEventDataHandler>().TotalData.ShouldBe(10);
} }

12
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBus_Exception_Test.cs → framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_Exception_Test.cs

@ -3,18 +3,18 @@ using System.Threading.Tasks;
using Shouldly; using Shouldly;
using Xunit; using Xunit;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus.Local
{ {
public class EventBus_Exception_Test : EventBusTestBase public class EventBus_Exception_Test : EventBusTestBase
{ {
[Fact] [Fact]
public async Task Should_Throw_Single_Exception_If_Only_One_Of_Handlers_Fails() public async Task Should_Throw_Single_Exception_If_Only_One_Of_Handlers_Fails()
{ {
EventBus.Register<MySimpleEventData>(eventData => throw new Exception("This exception is intentionally thrown!")); LocalEventBus.Subscribe<MySimpleEventData>(eventData => throw new Exception("This exception is intentionally thrown!"));
var appException = await Assert.ThrowsAsync<Exception>(async () => var appException = await Assert.ThrowsAsync<Exception>(async () =>
{ {
await EventBus.TriggerAsync(new MySimpleEventData(1)); await LocalEventBus.PublishAsync(new MySimpleEventData(1));
}); });
appException.Message.ShouldBe("This exception is intentionally thrown!"); appException.Message.ShouldBe("This exception is intentionally thrown!");
@ -23,15 +23,15 @@ namespace Volo.Abp.EventBus
[Fact] [Fact]
public async Task Should_Throw_Aggregate_Exception_If_More_Than_One_Of_Handlers_Fail() public async Task Should_Throw_Aggregate_Exception_If_More_Than_One_Of_Handlers_Fail()
{ {
EventBus.Register<MySimpleEventData>( LocalEventBus.Subscribe<MySimpleEventData>(
eventData => throw new Exception("This exception is intentionally thrown #1!")); eventData => throw new Exception("This exception is intentionally thrown #1!"));
EventBus.Register<MySimpleEventData>( LocalEventBus.Subscribe<MySimpleEventData>(
eventData => throw new Exception("This exception is intentionally thrown #2!")); eventData => throw new Exception("This exception is intentionally thrown #2!"));
var aggrException = await Assert.ThrowsAsync<AggregateException>(async () => var aggrException = await Assert.ThrowsAsync<AggregateException>(async () =>
{ {
await EventBus.TriggerAsync(new MySimpleEventData(1)); await LocalEventBus.PublishAsync(new MySimpleEventData(1));
}); });
aggrException.InnerExceptions.Count.ShouldBe(2); aggrException.InnerExceptions.Count.ShouldBe(2);

8
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventBus_MultipleHandle_Test.cs → framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/EventBus_MultipleHandle_Test.cs

@ -4,7 +4,7 @@ using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Domain.Entities.Events;
using Xunit; using Xunit;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus.Local
{ {
public class EventBus_EntityEvents_Test : EventBusTestBase public class EventBus_EntityEvents_Test : EventBusTestBase
{ {
@ -13,10 +13,10 @@ namespace Volo.Abp.EventBus
{ {
var handler = new MyEventHandler(); var handler = new MyEventHandler();
EventBus.Register<EntityChangedEventData<MyEntity>>(handler); LocalEventBus.Subscribe<EntityChangedEventData<MyEntity>>(handler);
EventBus.Register<EntityCreatedEventData<MyEntity>>(handler); LocalEventBus.Subscribe<EntityCreatedEventData<MyEntity>>(handler);
await EventBus.TriggerAsync(new EntityCreatedEventData<MyEntity>(new MyEntity())); await LocalEventBus.PublishAsync(new EntityCreatedEventData<MyEntity>(new MyEntity()));
handler.EntityCreatedEventCount.ShouldBe(1); handler.EntityCreatedEventCount.ShouldBe(1);
handler.EntityChangedEventCount.ShouldBe(1); handler.EntityChangedEventCount.ShouldBe(1);

10
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/GenericInheritanceTest.cs → framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/GenericInheritanceTest.cs

@ -4,7 +4,7 @@ using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Domain.Entities.Events;
using Xunit; using Xunit;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus.Local
{ {
public class GenericInheritanceTest : EventBusTestBase public class GenericInheritanceTest : EventBusTestBase
{ {
@ -13,7 +13,7 @@ namespace Volo.Abp.EventBus
{ {
var triggeredEvent = false; var triggeredEvent = false;
EventBus.Register<EntityChangedEventData<Person>>( LocalEventBus.Subscribe<EntityChangedEventData<Person>>(
eventData => eventData =>
{ {
eventData.Entity.Id.ShouldBe(42); eventData.Entity.Id.ShouldBe(42);
@ -21,7 +21,7 @@ namespace Volo.Abp.EventBus
return Task.CompletedTask; return Task.CompletedTask;
}); });
await EventBus.TriggerAsync(new EntityUpdatedEventData<Person>(new Person { Id = 42 })); await LocalEventBus.PublishAsync(new EntityUpdatedEventData<Person>(new Person { Id = 42 }));
triggeredEvent.ShouldBe(true); triggeredEvent.ShouldBe(true);
} }
@ -31,7 +31,7 @@ namespace Volo.Abp.EventBus
{ {
var triggeredEvent = false; var triggeredEvent = false;
EventBus.Register<EntityChangedEventData<Person>>( LocalEventBus.Subscribe<EntityChangedEventData<Person>>(
eventData => eventData =>
{ {
eventData.Entity.Id.ShouldBe(42); eventData.Entity.Id.ShouldBe(42);
@ -39,7 +39,7 @@ namespace Volo.Abp.EventBus
return Task.CompletedTask; return Task.CompletedTask;
}); });
await EventBus.TriggerAsync(new EntityChangedEventData<Student>(new Student { Id = 42 })); await LocalEventBus.PublishAsync(new EntityChangedEventData<Student>(new Student { Id = 42 }));
triggeredEvent.ShouldBe(true); triggeredEvent.ShouldBe(true);
} }

48
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/InheritanceTest.cs

@ -0,0 +1,48 @@
using System.Threading.Tasks;
using Xunit;
namespace Volo.Abp.EventBus.Local
{
public class InheritanceTest : EventBusTestBase
{
[Fact]
public async Task Should_Handle_Events_For_Derived_Classes()
{
var totalData = 0;
LocalEventBus.Subscribe<MySimpleEventData>(
eventData =>
{
totalData += eventData.Value;
return Task.CompletedTask;
});
await LocalEventBus.PublishAsync(new MySimpleEventData(1)); //Should handle directly registered class
await LocalEventBus.PublishAsync(new MySimpleEventData(2)); //Should handle directly registered class
await LocalEventBus.PublishAsync(new MyDerivedEventData(3)); //Should handle derived class too
await LocalEventBus.PublishAsync(new MyDerivedEventData(4)); //Should handle derived class too
Assert.Equal(10, totalData);
}
[Fact]
public async Task Should_Not_Handle_Events_For_Base_Classes()
{
var totalData = 0;
LocalEventBus.Subscribe<MyDerivedEventData>(
eventData =>
{
totalData += eventData.Value;
return Task.CompletedTask;
});
await LocalEventBus.PublishAsync(new MySimpleEventData(1)); //Should not handle
await LocalEventBus.PublishAsync(new MySimpleEventData(2)); //Should not handle
await LocalEventBus.PublishAsync(new MyDerivedEventData(3)); //Should handle
await LocalEventBus.PublishAsync(new MyDerivedEventData(4)); //Should handle
Assert.Equal(7, totalData);
}
}
}

2
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleEventDataHandler.cs → framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/MySimpleEventDataHandler.cs

@ -1,7 +1,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus.Local
{ {
public class MySimpleEventDataHandler : IEventHandler<MySimpleEventData>, ISingletonDependency public class MySimpleEventDataHandler : IEventHandler<MySimpleEventData>, ISingletonDependency
{ {

2
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/MySimpleTransientEventHandler.cs → framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/MySimpleTransientEventHandler.cs

@ -1,7 +1,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus.Local
{ {
public class MySimpleTransientEventHandler : IEventHandler<MySimpleEventData>, IDisposable public class MySimpleTransientEventHandler : IEventHandler<MySimpleEventData>, IDisposable
{ {

10
framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/TransientDisposableEventHandlerTest.cs → framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/Local/TransientDisposableEventHandlerTest.cs

@ -1,18 +1,18 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Xunit; using Xunit;
namespace Volo.Abp.EventBus namespace Volo.Abp.EventBus.Local
{ {
public class TransientDisposableEventHandlerTest : EventBusTestBase public class TransientDisposableEventHandlerTest : EventBusTestBase
{ {
[Fact] [Fact]
public async Task Should_Call_Handler_AndDispose() public async Task Should_Call_Handler_AndDispose()
{ {
EventBus.Register<MySimpleEventData, MySimpleTransientEventHandler>(); LocalEventBus.Subscribe<MySimpleEventData, MySimpleTransientEventHandler>();
await EventBus.TriggerAsync(new MySimpleEventData(1)); await LocalEventBus.PublishAsync(new MySimpleEventData(1));
await EventBus.TriggerAsync(new MySimpleEventData(2)); await LocalEventBus.PublishAsync(new MySimpleEventData(2));
await EventBus.TriggerAsync(new MySimpleEventData(3)); await LocalEventBus.PublishAsync(new MySimpleEventData(3));
Assert.Equal(3, MySimpleTransientEventHandler.HandleCount); Assert.Equal(3, MySimpleTransientEventHandler.HandleCount);
Assert.Equal(3, MySimpleTransientEventHandler.DisposeCount); Assert.Equal(3, MySimpleTransientEventHandler.DisposeCount);

7
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/DomainEvents_Tests.cs

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Shouldly; using Shouldly;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus; using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.TestApp.Domain; using Volo.Abp.TestApp.Domain;
using Xunit; using Xunit;
@ -14,12 +15,12 @@ namespace Volo.Abp.TestApp.Testing
where TStartupModule : IAbpModule where TStartupModule : IAbpModule
{ {
protected readonly IRepository<Person, Guid> PersonRepository; protected readonly IRepository<Person, Guid> PersonRepository;
protected readonly IEventBus EventBus; protected readonly ILocalEventBus LocalEventBus;
protected DomainEvents_Tests() protected DomainEvents_Tests()
{ {
PersonRepository = GetRequiredService<IRepository<Person, Guid>>(); PersonRepository = GetRequiredService<IRepository<Person, Guid>>();
EventBus = GetRequiredService<IEventBus>(); LocalEventBus = GetRequiredService<ILocalEventBus>();
} }
[Fact] [Fact]
@ -29,7 +30,7 @@ namespace Volo.Abp.TestApp.Testing
var isTriggered = false; var isTriggered = false;
EventBus.Register<PersonNameChangedEvent>(data => LocalEventBus.Subscribe<PersonNameChangedEvent>(data =>
{ {
data.OldName.ShouldBe("Douglas"); data.OldName.ShouldBe("Douglas");
data.Person.Name.ShouldBe("Douglas-Changed"); data.Person.Name.ShouldBe("Douglas-Changed");

13
framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs

@ -4,6 +4,7 @@ using Shouldly;
using Volo.Abp.Domain.Entities.Events; using Volo.Abp.Domain.Entities.Events;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.EventBus; using Volo.Abp.EventBus;
using Volo.Abp.EventBus.Local;
using Volo.Abp.Modularity; using Volo.Abp.Modularity;
using Volo.Abp.TestApp.Domain; using Volo.Abp.TestApp.Domain;
using Volo.Abp.Uow; using Volo.Abp.Uow;
@ -15,12 +16,12 @@ namespace Volo.Abp.TestApp.Testing
where TStartupModule : IAbpModule where TStartupModule : IAbpModule
{ {
protected IRepository<Person, Guid> PersonRepository { get; } protected IRepository<Person, Guid> PersonRepository { get; }
protected IEventBus EventBus { get; } protected ILocalEventBus LocalEventBus { get; }
protected EntityChangeEvents_Tests() protected EntityChangeEvents_Tests()
{ {
PersonRepository = GetRequiredService<IRepository<Person, Guid>>(); PersonRepository = GetRequiredService<IRepository<Person, Guid>>();
EventBus = GetRequiredService<IEventBus>(); LocalEventBus = GetRequiredService<ILocalEventBus>();
} }
[Fact] [Fact]
@ -35,7 +36,7 @@ namespace Volo.Abp.TestApp.Testing
using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin()) using (var uow = GetRequiredService<IUnitOfWorkManager>().Begin())
{ {
EventBus.Register<EntityCreatingEventData<Person>>(data => LocalEventBus.Subscribe<EntityCreatingEventData<Person>>(data =>
{ {
creatingEventTriggered.ShouldBeFalse(); creatingEventTriggered.ShouldBeFalse();
createdEventTriggered.ShouldBeFalse(); createdEventTriggered.ShouldBeFalse();
@ -54,7 +55,7 @@ namespace Volo.Abp.TestApp.Testing
return Task.CompletedTask; return Task.CompletedTask;
}); });
EventBus.Register<EntityCreatedEventData<Person>>(data => LocalEventBus.Subscribe<EntityCreatedEventData<Person>>(data =>
{ {
creatingEventTriggered.ShouldBeTrue(); creatingEventTriggered.ShouldBeTrue();
createdEventTriggered.ShouldBeFalse(); createdEventTriggered.ShouldBeFalse();
@ -69,7 +70,7 @@ namespace Volo.Abp.TestApp.Testing
return Task.CompletedTask; return Task.CompletedTask;
}); });
EventBus.Register<EntityUpdatingEventData<Person>>(data => LocalEventBus.Subscribe<EntityUpdatingEventData<Person>>(data =>
{ {
creatingEventTriggered.ShouldBeTrue(); creatingEventTriggered.ShouldBeTrue();
createdEventTriggered.ShouldBeFalse(); createdEventTriggered.ShouldBeFalse();
@ -84,7 +85,7 @@ namespace Volo.Abp.TestApp.Testing
return Task.CompletedTask; return Task.CompletedTask;
}); });
EventBus.Register<EntityUpdatedEventData<Person>>(data => LocalEventBus.Subscribe<EntityUpdatedEventData<Person>>(data =>
{ {
creatingEventTriggered.ShouldBeTrue(); creatingEventTriggered.ShouldBeTrue();
createdEventTriggered.ShouldBeTrue(); createdEventTriggered.ShouldBeTrue();

Loading…
Cancel
Save