From 7f464754beaaee73342f81157a8fe9c78b44da64 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Fri, 7 Jan 2022 21:38:19 +0800 Subject: [PATCH 1/7] Enhancement Trigger Event Handler --- .../Azure/AzureDistributedEventBus.cs | 6 +- .../Kafka/KafkaDistributedEventBus.cs | 6 +- .../RabbitMq/RabbitMqDistributedEventBus.cs | 6 +- .../Rebus/RebusDistributedEventBus.cs | 6 +- .../Distributed/DistributedEventBusBase.cs | 6 +- .../Volo/Abp/EventBus/EventBusBase.cs | 28 ++------ .../Volo/Abp/EventBus/EventHandlerInvoker.cs | 26 ++++++++ .../EventBus/EventHandlerMethodExecutor.cs | 66 +++++++++++++++++++ .../Volo/Abp/EventBus/IEventHandlerInvoker.cs | 8 +++ .../Volo/Abp/EventBus/Local/LocalEventBus.cs | 5 +- 10 files changed, 130 insertions(+), 33 deletions(-) create mode 100644 framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs create mode 100644 framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerMethodExecutor.cs create mode 100644 framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventHandlerInvoker.cs diff --git a/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AzureDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AzureDistributedEventBus.cs index 49add5df84..18fb5c6e9e 100644 --- a/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AzureDistributedEventBus.cs +++ b/framework/src/Volo.Abp.EventBus.Azure/Volo/Abp/EventBus/Azure/AzureDistributedEventBus.cs @@ -39,13 +39,15 @@ public class AzureDistributedEventBus : DistributedEventBusBase, ISingletonDepen IOptions abpAzureEventBusOptions, IAzureServiceBusSerializer serializer, IAzureServiceBusMessageConsumerFactory messageConsumerFactory, - IPublisherPool publisherPool) + IPublisherPool publisherPool, + IEventHandlerInvoker eventHandlerInvoker) : base(serviceScopeFactory, currentTenant, unitOfWorkManager, abpDistributedEventBusOptions, guidGenerator, - clock) + clock, + eventHandlerInvoker) { _options = abpAzureEventBusOptions.Value; _serializer = serializer; diff --git a/framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs index ec1901161a..a19b01311c 100644 --- a/framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs +++ b/framework/src/Volo.Abp.EventBus.Kafka/Volo/Abp/EventBus/Kafka/KafkaDistributedEventBus.cs @@ -39,14 +39,16 @@ public class KafkaDistributedEventBus : DistributedEventBusBase, ISingletonDepen IKafkaSerializer serializer, IProducerPool producerPool, IGuidGenerator guidGenerator, - IClock clock) + IClock clock, + IEventHandlerInvoker eventHandlerInvoker) : base( serviceScopeFactory, currentTenant, unitOfWorkManager, abpDistributedEventBusOptions, guidGenerator, - clock) + clock, + eventHandlerInvoker) { AbpKafkaEventBusOptions = abpKafkaEventBusOptions.Value; MessageConsumerFactory = messageConsumerFactory; diff --git a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs index 1564cfd213..38f8e4eea5 100644 --- a/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs +++ b/framework/src/Volo.Abp.EventBus.RabbitMQ/Volo/Abp/EventBus/RabbitMq/RabbitMqDistributedEventBus.cs @@ -44,14 +44,16 @@ public class RabbitMqDistributedEventBus : DistributedEventBusBase, ISingletonDe ICurrentTenant currentTenant, IUnitOfWorkManager unitOfWorkManager, IGuidGenerator guidGenerator, - IClock clock) + IClock clock, + IEventHandlerInvoker eventHandlerInvoker) : base( serviceScopeFactory, currentTenant, unitOfWorkManager, distributedEventBusOptions, guidGenerator, - clock) + clock, + eventHandlerInvoker) { ConnectionPool = connectionPool; Serializer = serializer; diff --git a/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs b/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs index 0542a705eb..d637f3fe89 100644 --- a/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs +++ b/framework/src/Volo.Abp.EventBus.Rebus/Volo/Abp/EventBus/Rebus/RebusDistributedEventBus.cs @@ -38,14 +38,16 @@ public class RebusDistributedEventBus : DistributedEventBusBase, ISingletonDepen IOptions abpEventBusRebusOptions, IRebusSerializer serializer, IGuidGenerator guidGenerator, - IClock clock) : + IClock clock, + IEventHandlerInvoker eventHandlerInvoker) : base( serviceScopeFactory, currentTenant, unitOfWorkManager, abpDistributedEventBusOptions, guidGenerator, - clock) + clock, + eventHandlerInvoker) { Rebus = rebus; Serializer = serializer; diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs index dee9d1f7d3..6547c1686c 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/DistributedEventBusBase.cs @@ -22,11 +22,13 @@ public abstract class DistributedEventBusBase : EventBusBase, IDistributedEventB IUnitOfWorkManager unitOfWorkManager, IOptions abpDistributedEventBusOptions, IGuidGenerator guidGenerator, - IClock clock + IClock clock, + IEventHandlerInvoker eventHandlerInvoker ) : base( serviceScopeFactory, currentTenant, - unitOfWorkManager) + unitOfWorkManager, + eventHandlerInvoker) { GuidGenerator = guidGenerator; Clock = clock; diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs index 15b597da30..b6805de1ae 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs @@ -22,14 +22,18 @@ public abstract class EventBusBase : IEventBus protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected IEventHandlerInvoker EventHandlerInvoker { get; } + protected EventBusBase( IServiceScopeFactory serviceScopeFactory, ICurrentTenant currentTenant, - IUnitOfWorkManager unitOfWorkManager) + IUnitOfWorkManager unitOfWorkManager, + IEventHandlerInvoker eventHandlerInvoker) { ServiceScopeFactory = serviceScopeFactory; CurrentTenant = currentTenant; UnitOfWorkManager = unitOfWorkManager; + EventHandlerInvoker = eventHandlerInvoker; } /// @@ -210,27 +214,9 @@ public abstract class EventBusBase : IEventBus using (CurrentTenant.Change(GetEventDataTenantId(eventData))) { - if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(ILocalEventHandler<>))) - { - var method = typeof(ILocalEventHandler<>) - .MakeGenericType(eventType) - .GetMethod( - nameof(ILocalEventHandler.HandleEventAsync), - new[] { eventType } - ); - - await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData })); - } - else if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IDistributedEventHandler<>))) + if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(ILocalEventHandler<>)) || ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IDistributedEventHandler<>))) { - var method = typeof(IDistributedEventHandler<>) - .MakeGenericType(eventType) - .GetMethod( - nameof(IDistributedEventHandler.HandleEventAsync), - new[] { eventType } - ); - - await ((Task)method.Invoke(eventHandlerWrapper.EventHandler, new[] { eventData })); + await EventHandlerInvoker.InvokeAsync(eventHandlerWrapper.EventHandler, eventData); } else { diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs new file mode 100644 index 0000000000..986615e134 --- /dev/null +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Concurrent; +using System.Reflection; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.EventBus; + +public class EventHandlerInvoker : IEventHandlerInvoker, ISingletonDependency +{ + private const string EventHandlerMethodName = "HandleEventAsync"; + private readonly ConcurrentDictionary _executorCache; + + public EventHandlerInvoker() + { + _executorCache = new ConcurrentDictionary(); + } + + public Task InvokeAsync(IEventHandler eventHandler, object eventData) + { + var handleType = eventHandler.GetType(); + var executor = _executorCache.GetOrAdd(handleType, type => EventHandlerMethodExecutor.Create(type.GetMethod(EventHandlerMethodName), type.GetTypeInfo())); + + return executor.ExecuteAsync(eventHandler, new[] { eventData }); + } +} diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerMethodExecutor.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerMethodExecutor.cs new file mode 100644 index 0000000000..332b9af1ef --- /dev/null +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerMethodExecutor.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.Linq.Expressions; +using System.Reflection; +using System.Threading.Tasks; + +namespace Volo.Abp.EventBus; + +public class EventHandlerMethodExecutor +{ + private readonly MethodExecutorAsync _executorAsync; + private delegate Task MethodExecutorAsync(IEventHandler target, object[] parameters); + + public MethodInfo MethodInfo { get; } + + public TypeInfo TargetTypeInfo { get; } + + private EventHandlerMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) + { + if (methodInfo == null) + { + throw new ArgumentNullException(nameof(methodInfo)); + } + + MethodInfo = methodInfo; + TargetTypeInfo = targetTypeInfo; + + _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo); + } + + private static MethodExecutorAsync GetExecutorAsync(MethodInfo methodInfo, TypeInfo targetTypeInfo) + { + var targetParameter = Expression.Parameter(typeof(IEventHandler), "target"); + var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); + + var paramInfos = methodInfo.GetParameters(); + var parameters = new List(paramInfos.Length); + + for (var i = 0; i < paramInfos.Length; i++) + { + var paramInfo = paramInfos[i]; + var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); + var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); + + parameters.Add(valueCast); + } + + var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); + var methodCall = Expression.Call(instanceCast, methodInfo, parameters); + + var castMethodCall = Expression.Convert(methodCall, typeof(Task)); + var lambda = Expression.Lambda(castMethodCall, targetParameter, parametersParameter); + return lambda.Compile(); + + } + + public static EventHandlerMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) + { + return new EventHandlerMethodExecutor(methodInfo, targetTypeInfo); + } + + public Task ExecuteAsync(IEventHandler target, object[] parameters) + { + return _executorAsync(target, parameters); + } +} diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventHandlerInvoker.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventHandlerInvoker.cs new file mode 100644 index 0000000000..37019cce62 --- /dev/null +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventHandlerInvoker.cs @@ -0,0 +1,8 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.EventBus; + +public interface IEventHandlerInvoker +{ + Task InvokeAsync(IEventHandler eventHandler, object eventData); +} diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs index 800c4a61df..3f7dbbc851 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/LocalEventBus.cs @@ -33,8 +33,9 @@ public class LocalEventBus : EventBusBase, ILocalEventBus, ISingletonDependency IOptions options, IServiceScopeFactory serviceScopeFactory, ICurrentTenant currentTenant, - IUnitOfWorkManager unitOfWorkManager) - : base(serviceScopeFactory, currentTenant, unitOfWorkManager) + IUnitOfWorkManager unitOfWorkManager, + IEventHandlerInvoker eventHandlerInvoker) + : base(serviceScopeFactory, currentTenant, unitOfWorkManager, eventHandlerInvoker) { Options = options.Value; Logger = NullLogger.Instance; From fd10ddb9d3644c00cbe3a0bf2eb4e750e667f2ef Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sat, 8 Jan 2022 11:09:57 +0800 Subject: [PATCH 2/7] Update EventHandlerInvoker --- .../Volo/Abp/EventBus/EventBusBase.cs | 2 +- .../Volo/Abp/EventBus/EventHandlerInvoker.cs | 17 +++++++++++++---- .../Volo/Abp/EventBus/IEventHandlerInvoker.cs | 5 +++-- .../MyProjectNameModule.cs | 4 +++- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs index b6805de1ae..bc307a1777 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs @@ -216,7 +216,7 @@ public abstract class EventBusBase : IEventBus { if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(ILocalEventHandler<>)) || ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IDistributedEventHandler<>))) { - await EventHandlerInvoker.InvokeAsync(eventHandlerWrapper.EventHandler, eventData); + await EventHandlerInvoker.InvokeAsync(eventHandlerWrapper.EventHandler, eventData, eventType); } else { diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs index 986615e134..35d1f4e74c 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Linq; using System.Reflection; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; @@ -9,18 +10,26 @@ namespace Volo.Abp.EventBus; public class EventHandlerInvoker : IEventHandlerInvoker, ISingletonDependency { private const string EventHandlerMethodName = "HandleEventAsync"; - private readonly ConcurrentDictionary _executorCache; + private readonly ConcurrentDictionary _executorCache; public EventHandlerInvoker() { - _executorCache = new ConcurrentDictionary(); + _executorCache = new ConcurrentDictionary(); } - public Task InvokeAsync(IEventHandler eventHandler, object eventData) + public Task InvokeAsync(IEventHandler eventHandler, object eventData, Type eventType) { var handleType = eventHandler.GetType(); - var executor = _executorCache.GetOrAdd(handleType, type => EventHandlerMethodExecutor.Create(type.GetMethod(EventHandlerMethodName), type.GetTypeInfo())); + var key = $"{handleType.FullName}_{eventType.FullName}"; + + var executor = _executorCache.GetOrAdd(key, _ => EventHandlerMethodExecutor.Create(GetHandleEventMethodInfo(handleType, eventType), handleType.GetTypeInfo())); return executor.ExecuteAsync(eventHandler, new[] { eventData }); } + + private static MethodInfo GetHandleEventMethodInfo(Type handleType, Type eventType) + { + var methods = handleType.GetMethods().Where(x => x.Name == EventHandlerMethodName).ToArray(); + return methods.Length == 1 ? methods.First() : methods.FirstOrDefault(x => x.GetParameters().Any(param => param.ParameterType == eventType)); + } } diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventHandlerInvoker.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventHandlerInvoker.cs index 37019cce62..71c2bb552c 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventHandlerInvoker.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/IEventHandlerInvoker.cs @@ -1,8 +1,9 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; namespace Volo.Abp.EventBus; public interface IEventHandlerInvoker { - Task InvokeAsync(IEventHandler eventHandler, object eventData); + Task InvokeAsync(IEventHandler eventHandler, object eventData, Type eventType); } diff --git a/templates/console/src/MyCompanyName.MyProjectName/MyProjectNameModule.cs b/templates/console/src/MyCompanyName.MyProjectName/MyProjectNameModule.cs index c7045a62ea..e2fcf991a6 100644 --- a/templates/console/src/MyCompanyName.MyProjectName/MyProjectNameModule.cs +++ b/templates/console/src/MyCompanyName.MyProjectName/MyProjectNameModule.cs @@ -5,12 +5,14 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Volo.Abp; using Volo.Abp.Autofac; +using Volo.Abp.EventBus; using Volo.Abp.Modularity; namespace MyCompanyName.MyProjectName; [DependsOn( - typeof(AbpAutofacModule) + typeof(AbpAutofacModule), + typeof(AbpEventBusModule) )] public class MyProjectNameModule : AbpModule { From 8bd80895c2f4b5f277a6463f731b2a87d0a34dc4 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sat, 8 Jan 2022 14:10:29 +0800 Subject: [PATCH 3/7] Update MyProjectNameModule --- .../src/MyCompanyName.MyProjectName/MyProjectNameModule.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/templates/console/src/MyCompanyName.MyProjectName/MyProjectNameModule.cs b/templates/console/src/MyCompanyName.MyProjectName/MyProjectNameModule.cs index e2fcf991a6..c7045a62ea 100644 --- a/templates/console/src/MyCompanyName.MyProjectName/MyProjectNameModule.cs +++ b/templates/console/src/MyCompanyName.MyProjectName/MyProjectNameModule.cs @@ -5,14 +5,12 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Volo.Abp; using Volo.Abp.Autofac; -using Volo.Abp.EventBus; using Volo.Abp.Modularity; namespace MyCompanyName.MyProjectName; [DependsOn( - typeof(AbpAutofacModule), - typeof(AbpEventBusModule) + typeof(AbpAutofacModule) )] public class MyProjectNameModule : AbpModule { From 60c5464fa6ad15d57d4f54a14b3e32f86572a572 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 10 Jan 2022 15:01:27 +0800 Subject: [PATCH 4/7] Add unit test --- .../Abp/EventBus/EventHandlerInvoker_Tests.cs | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventHandlerInvoker_Tests.cs diff --git a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventHandlerInvoker_Tests.cs b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventHandlerInvoker_Tests.cs new file mode 100644 index 0000000000..eb02114dc6 --- /dev/null +++ b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventHandlerInvoker_Tests.cs @@ -0,0 +1,127 @@ +using System; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.Domain.Entities.Events.Distributed; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.EventBus.Local; +using Xunit; + +namespace Volo.Abp.EventBus; + +public class EventHandlerInvoker_Tests : EventBusTestBase +{ + private readonly IEventHandlerInvoker _eventHandlerInvoker; + + public EventHandlerInvoker_Tests() + { + _eventHandlerInvoker = GetRequiredService(); + } + + [Fact] + public async Task Should_Invoke_LocalEventHandler_With_MyEventData() + { + var localHandler = new MyLocalEventHandler(); + var eventData = new MyEventData(); + + await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, eventData.GetType()); + + localHandler.MyEventDataCount.ShouldBe(1); + localHandler.EntityChangedEventDataCount.ShouldBe(0); + localHandler.EntityChangedEventDataCount.ShouldBe(0); + } + + [Fact] + public async Task Should_Invoke_LocalEventHandler_Created_And_Changed_Once() + { + var localHandler = new MyLocalEventHandler(); + var eventData = new EntityCreatedEventData(new MyEntity()); + + await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, eventData.GetType()); + await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, typeof(EntityChangedEventData)); + + localHandler.MyEventDataCount.ShouldBe(0); + localHandler.EntityChangedEventDataCount.ShouldBe(1); + localHandler.EntityChangedEventDataCount.ShouldBe(1); + } + + [Fact] + public async Task Should_Invoke_DistributedEventHandler_With_MyEventData() + { + var localHandler = new MyDistributedEventHandler(); + var eventData = new MyEventData(); + + await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, eventData.GetType()); + + localHandler.MyEventDataCount.ShouldBe(1); + localHandler.EntityCreatedCount.ShouldBe(0); + } + + [Fact] + public async Task Should_Invoke_DistributedEventHandler_With_EntityCreatedEto() + { + var localHandler = new MyDistributedEventHandler(); + var eventData = new EntityCreatedEto(new MyEntity()); + + await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, eventData.GetType()); + + localHandler.MyEventDataCount.ShouldBe(0); + localHandler.EntityCreatedCount.ShouldBe(1); + } + + public class MyEventData + { + } + + public class MyEntity : Entity + { + + } + + public class MyDistributedEventHandler : IDistributedEventHandler, + IDistributedEventHandler> + { + public int MyEventDataCount { get; set; } + public int EntityCreatedCount { get; set; } + + public Task HandleEventAsync(MyEventData eventData) + { + MyEventDataCount++; + return Task.CompletedTask; + } + + public Task HandleEventAsync(EntityCreatedEto eventData) + { + EntityCreatedCount++; + return Task.CompletedTask; + } + } + + public class MyLocalEventHandler : ILocalEventHandler, + IDistributedEventHandler>, + IDistributedEventHandler> + { + public int MyEventDataCount { get; set; } + public int EntityCreatedEventDataCount { get; set; } + public int EntityChangedEventDataCount { get; set; } + + public Task HandleEventAsync(MyEventData eventData) + { + MyEventDataCount++; + return Task.CompletedTask; + } + + public Task HandleEventAsync(EntityCreatedEventData eventData) + { + EntityCreatedEventDataCount++; + return Task.CompletedTask; + } + + public Task HandleEventAsync(EntityChangedEventData eventData) + { + EntityChangedEventDataCount++; + return Task.CompletedTask; + } + } +} From f0a8362135fd53e44ee3ac444bcdb00241743630 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 10 Jan 2022 15:57:47 +0800 Subject: [PATCH 5/7] Refactor. --- .../Volo/Abp/EventBus/EventBusBase.cs | 9 +-- .../Volo/Abp/EventBus/EventHandlerInvoker.cs | 37 +++++----- .../EventBus/EventHandlerMethodExecutor.cs | 70 +++++-------------- .../Abp/EventBus/EventHandlerInvoker_Tests.cs | 3 +- 4 files changed, 43 insertions(+), 76 deletions(-) diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs index bc307a1777..6e48b27e5f 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventBusBase.cs @@ -214,14 +214,7 @@ public abstract class EventBusBase : IEventBus using (CurrentTenant.Change(GetEventDataTenantId(eventData))) { - if (ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(ILocalEventHandler<>)) || ReflectionHelper.IsAssignableToGenericType(handlerType, typeof(IDistributedEventHandler<>))) - { - await EventHandlerInvoker.InvokeAsync(eventHandlerWrapper.EventHandler, eventData, eventType); - } - else - { - throw new AbpException("The object instance is not an event handler. Object type: " + handlerType.AssemblyQualifiedName); - } + await EventHandlerInvoker.InvokeAsync(eventHandlerWrapper.EventHandler, eventData, eventType); } } catch (TargetInvocationException ex) diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs index 35d1f4e74c..6269500687 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs @@ -1,35 +1,40 @@ using System; using System.Collections.Concurrent; -using System.Linq; -using System.Reflection; using System.Threading.Tasks; using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; namespace Volo.Abp.EventBus; public class EventHandlerInvoker : IEventHandlerInvoker, ISingletonDependency { - private const string EventHandlerMethodName = "HandleEventAsync"; - private readonly ConcurrentDictionary _executorCache; + private readonly ConcurrentDictionary _cache; public EventHandlerInvoker() { - _executorCache = new ConcurrentDictionary(); + _cache = new ConcurrentDictionary(); } - public Task InvokeAsync(IEventHandler eventHandler, object eventData, Type eventType) + public async Task InvokeAsync(IEventHandler eventHandler, object eventData, Type eventType) { - var handleType = eventHandler.GetType(); - var key = $"{handleType.FullName}_{eventType.FullName}"; + if (typeof(ILocalEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) + { + var eventHandlerCall = _cache.GetOrAdd($"{typeof(LocalEventHandlerMethodExecutor<>).FullName}{eventHandler.GetType().FullName}-{eventType.FullName}", + (_) => (IEventHandlerMethodExecutor)Activator.CreateInstance(typeof(LocalEventHandlerMethodExecutor<>).MakeGenericType(eventType))); + await eventHandlerCall.ExecutorAsync(eventHandler, eventData); + } - var executor = _executorCache.GetOrAdd(key, _ => EventHandlerMethodExecutor.Create(GetHandleEventMethodInfo(handleType, eventType), handleType.GetTypeInfo())); + if (typeof(IDistributedEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) + { + var eventHandlerCall = _cache.GetOrAdd($"{typeof(DistributedEventHandlerMethodExecutor<>).FullName}{eventHandler.GetType().FullName}-{eventType.FullName}", + (_) => (IEventHandlerMethodExecutor)Activator.CreateInstance(typeof(DistributedEventHandlerMethodExecutor<>).MakeGenericType(eventType))); + await eventHandlerCall.ExecutorAsync(eventHandler, eventData); + } - return executor.ExecuteAsync(eventHandler, new[] { eventData }); - } - - private static MethodInfo GetHandleEventMethodInfo(Type handleType, Type eventType) - { - var methods = handleType.GetMethods().Where(x => x.Name == EventHandlerMethodName).ToArray(); - return methods.Length == 1 ? methods.First() : methods.FirstOrDefault(x => x.GetParameters().Any(param => param.ParameterType == eventType)); + if (!typeof(ILocalEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler) && + !typeof(IDistributedEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) + { + throw new AbpException("The object instance is not an event handler. Object type: " + eventHandler.GetType().AssemblyQualifiedName); + } } } diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerMethodExecutor.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerMethodExecutor.cs index 332b9af1ef..accbe513b2 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerMethodExecutor.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerMethodExecutor.cs @@ -1,66 +1,34 @@ using System; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Reflection; using System.Threading.Tasks; +using Volo.Abp.EventBus.Distributed; namespace Volo.Abp.EventBus; -public class EventHandlerMethodExecutor -{ - private readonly MethodExecutorAsync _executorAsync; - private delegate Task MethodExecutorAsync(IEventHandler target, object[] parameters); +public delegate Task EventHandlerMethodExecutorAsync(IEventHandler target, object parameter); - public MethodInfo MethodInfo { get; } +public interface IEventHandlerMethodExecutor +{ + EventHandlerMethodExecutorAsync ExecutorAsync { get; } +} - public TypeInfo TargetTypeInfo { get; } +public class LocalEventHandlerMethodExecutor : IEventHandlerMethodExecutor + where TEvent : class +{ + public EventHandlerMethodExecutorAsync ExecutorAsync => (target, parameter) => target.As>().HandleEventAsync(parameter.As()); - private EventHandlerMethodExecutor(MethodInfo methodInfo, TypeInfo targetTypeInfo) + public Task ExecuteAsync(IEventHandler target, TEvent parameters) { - if (methodInfo == null) - { - throw new ArgumentNullException(nameof(methodInfo)); - } - - MethodInfo = methodInfo; - TargetTypeInfo = targetTypeInfo; - - _executorAsync = GetExecutorAsync(methodInfo, targetTypeInfo); - } - - private static MethodExecutorAsync GetExecutorAsync(MethodInfo methodInfo, TypeInfo targetTypeInfo) - { - var targetParameter = Expression.Parameter(typeof(IEventHandler), "target"); - var parametersParameter = Expression.Parameter(typeof(object[]), "parameters"); - - var paramInfos = methodInfo.GetParameters(); - var parameters = new List(paramInfos.Length); - - for (var i = 0; i < paramInfos.Length; i++) - { - var paramInfo = paramInfos[i]; - var valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i)); - var valueCast = Expression.Convert(valueObj, paramInfo.ParameterType); - - parameters.Add(valueCast); - } - - var instanceCast = Expression.Convert(targetParameter, targetTypeInfo.AsType()); - var methodCall = Expression.Call(instanceCast, methodInfo, parameters); - - var castMethodCall = Expression.Convert(methodCall, typeof(Task)); - var lambda = Expression.Lambda(castMethodCall, targetParameter, parametersParameter); - return lambda.Compile(); - + return ExecutorAsync(target, parameters); } +} - public static EventHandlerMethodExecutor Create(MethodInfo methodInfo, TypeInfo targetTypeInfo) - { - return new EventHandlerMethodExecutor(methodInfo, targetTypeInfo); - } +public class DistributedEventHandlerMethodExecutor : IEventHandlerMethodExecutor + where TEvent : class +{ + public EventHandlerMethodExecutorAsync ExecutorAsync => (target, parameter) => target.As>().HandleEventAsync(parameter.As()); - public Task ExecuteAsync(IEventHandler target, object[] parameters) + public Task ExecuteAsync(IEventHandler target, TEvent parameters) { - return _executorAsync(target, parameters); + return ExecutorAsync(target, parameters); } } diff --git a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventHandlerInvoker_Tests.cs b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventHandlerInvoker_Tests.cs index eb02114dc6..294debbb09 100644 --- a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventHandlerInvoker_Tests.cs +++ b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/EventHandlerInvoker_Tests.cs @@ -27,7 +27,7 @@ public class EventHandlerInvoker_Tests : EventBusTestBase await _eventHandlerInvoker.InvokeAsync(localHandler, eventData, eventData.GetType()); - localHandler.MyEventDataCount.ShouldBe(1); + localHandler.MyEventDataCount.ShouldBe(2); localHandler.EntityChangedEventDataCount.ShouldBe(0); localHandler.EntityChangedEventDataCount.ShouldBe(0); } @@ -99,6 +99,7 @@ public class EventHandlerInvoker_Tests : EventBusTestBase } public class MyLocalEventHandler : ILocalEventHandler, + IDistributedEventHandler, IDistributedEventHandler>, IDistributedEventHandler> { From 0ef71d75c3763e96ec6ccd19beb0bb3a431a448c Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 10 Jan 2022 17:03:59 +0800 Subject: [PATCH 6/7] Add `notAnEventHandler`. --- .../Volo/Abp/EventBus/EventHandlerInvoker.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs index 6269500687..a75b1bb3fa 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs @@ -17,11 +17,13 @@ public class EventHandlerInvoker : IEventHandlerInvoker, ISingletonDependency public async Task InvokeAsync(IEventHandler eventHandler, object eventData, Type eventType) { + var notAnEventHandler = true; if (typeof(ILocalEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) { var eventHandlerCall = _cache.GetOrAdd($"{typeof(LocalEventHandlerMethodExecutor<>).FullName}{eventHandler.GetType().FullName}-{eventType.FullName}", (_) => (IEventHandlerMethodExecutor)Activator.CreateInstance(typeof(LocalEventHandlerMethodExecutor<>).MakeGenericType(eventType))); await eventHandlerCall.ExecutorAsync(eventHandler, eventData); + notAnEventHandler = false; } if (typeof(IDistributedEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) @@ -29,10 +31,10 @@ public class EventHandlerInvoker : IEventHandlerInvoker, ISingletonDependency var eventHandlerCall = _cache.GetOrAdd($"{typeof(DistributedEventHandlerMethodExecutor<>).FullName}{eventHandler.GetType().FullName}-{eventType.FullName}", (_) => (IEventHandlerMethodExecutor)Activator.CreateInstance(typeof(DistributedEventHandlerMethodExecutor<>).MakeGenericType(eventType))); await eventHandlerCall.ExecutorAsync(eventHandler, eventData); + notAnEventHandler = false; } - if (!typeof(ILocalEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler) && - !typeof(IDistributedEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) + if (notAnEventHandler) { throw new AbpException("The object instance is not an event handler. Object type: " + eventHandler.GetType().AssemblyQualifiedName); } From ee1fbf82ed12dc0cb371cda1c7602594c6fae639 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 10 Jan 2022 21:38:06 +0800 Subject: [PATCH 7/7] Optimize performance. --- .../Volo/Abp/EventBus/EventHandlerInvoker.cs | 38 ++++++++++++------- .../EventBus/EventHandlerInvokerCacheItem.cs | 8 ++++ 2 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvokerCacheItem.cs diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs index a75b1bb3fa..5c7517cfe1 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvoker.cs @@ -8,33 +8,43 @@ namespace Volo.Abp.EventBus; public class EventHandlerInvoker : IEventHandlerInvoker, ISingletonDependency { - private readonly ConcurrentDictionary _cache; + private readonly ConcurrentDictionary _cache; public EventHandlerInvoker() { - _cache = new ConcurrentDictionary(); + _cache = new ConcurrentDictionary(); } public async Task InvokeAsync(IEventHandler eventHandler, object eventData, Type eventType) { - var notAnEventHandler = true; - if (typeof(ILocalEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) + var cacheItem = _cache.GetOrAdd($"{eventHandler.GetType().FullName}-{eventType.FullName}", _ => { - var eventHandlerCall = _cache.GetOrAdd($"{typeof(LocalEventHandlerMethodExecutor<>).FullName}{eventHandler.GetType().FullName}-{eventType.FullName}", - (_) => (IEventHandlerMethodExecutor)Activator.CreateInstance(typeof(LocalEventHandlerMethodExecutor<>).MakeGenericType(eventType))); - await eventHandlerCall.ExecutorAsync(eventHandler, eventData); - notAnEventHandler = false; + var item = new EventHandlerInvokerCacheItem(); + + if (typeof(ILocalEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) + { + item.Local = (IEventHandlerMethodExecutor)Activator.CreateInstance(typeof(LocalEventHandlerMethodExecutor<>).MakeGenericType(eventType)); + } + + if (typeof(IDistributedEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) + { + item.Distributed = (IEventHandlerMethodExecutor)Activator.CreateInstance(typeof(DistributedEventHandlerMethodExecutor<>).MakeGenericType(eventType)); + } + + return item; + }); + + if (cacheItem.Local != null) + { + await cacheItem.Local.ExecutorAsync(eventHandler, eventData); } - if (typeof(IDistributedEventHandler<>).MakeGenericType(eventType).IsInstanceOfType(eventHandler)) + if (cacheItem.Distributed != null) { - var eventHandlerCall = _cache.GetOrAdd($"{typeof(DistributedEventHandlerMethodExecutor<>).FullName}{eventHandler.GetType().FullName}-{eventType.FullName}", - (_) => (IEventHandlerMethodExecutor)Activator.CreateInstance(typeof(DistributedEventHandlerMethodExecutor<>).MakeGenericType(eventType))); - await eventHandlerCall.ExecutorAsync(eventHandler, eventData); - notAnEventHandler = false; + await cacheItem.Distributed.ExecutorAsync(eventHandler, eventData); } - if (notAnEventHandler) + if (cacheItem.Local == null && cacheItem.Distributed == null) { throw new AbpException("The object instance is not an event handler. Object type: " + eventHandler.GetType().AssemblyQualifiedName); } diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvokerCacheItem.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvokerCacheItem.cs new file mode 100644 index 0000000000..0ec617afca --- /dev/null +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/EventHandlerInvokerCacheItem.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.EventBus; + +public class EventHandlerInvokerCacheItem +{ + public IEventHandlerMethodExecutor Local { get; set; } + + public IEventHandlerMethodExecutor Distributed { get; set; } +}