From 127f389819e210dcf6e90964cb5d9f22fdb2998e Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 16 Nov 2018 14:34:47 +0300 Subject: [PATCH] Trigger distributed events on entity changes --- .../Volo.Abp.Ddd.Domain.csproj | 1 + .../Volo/Abp/Domain/AbpDddDomainModule.cs | 5 +- .../Events/Distributed/EntityCreatedEto.cs | 17 ++++ .../Events/Distributed/EntityDeletedEto.cs | 17 ++++ .../Entities/Events/Distributed/EntityEto.cs | 20 +++++ .../Events/Distributed/EntityToEtoMapper.cs | 46 ++++++++++ .../Events/Distributed/EntityUpdatedEto.cs | 17 ++++ .../Entities/Events/Distributed/EtoBase.cs | 16 ++++ .../Events/Distributed/IEntityToEtoMapper.cs | 7 ++ .../Entities/Events/Distributed/Temp.cs | 29 ++++++ .../Events/EntityChangeEventHelper.cs | 57 ++++++------ .../Events/IEntityChangeEventHelper.cs | 3 - .../Distributed/EtoMappingDictionary.cs | 13 +++ .../Distributed/LocalEventBusOptions.cs | 2 + .../EventBus/Distributed/NullLocalEventBus.cs | 90 +++++++++++++++++++ .../Abp/EventBus/GenericEventNameAttribute.cs | 20 ++++- .../EventBus/{ => Local}/NullLocalEventBus.cs | 3 +- .../ObjectMapping/ObjectMapperExtensions.cs | 45 ++++++++++ .../ObjectMapperExtensions_Tests.cs | 26 ++++++ .../GenericEventNameAttribute_Tests.cs | 18 +++- .../Volo/Abp/TestApp/Domain/Person.cs | 2 + .../Volo/Abp/TestApp/Domain/PersonEto.cs | 17 ++++ .../Volo/Abp/TestApp/TestAppModule.cs | 16 +++- .../Testing/EntityChangeEvents_Tests.cs | 28 +++++- 24 files changed, 469 insertions(+), 46 deletions(-) create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityCreatedEto.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityDeletedEto.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityToEtoMapper.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityUpdatedEto.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EtoBase.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityToEtoMapper.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/Temp.cs create mode 100644 framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/EtoMappingDictionary.cs create mode 100644 framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/NullLocalEventBus.cs rename framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/{ => Local}/NullLocalEventBus.cs (97%) create mode 100644 framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/ObjectMapperExtensions.cs create mode 100644 framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/ObjectMapperExtensions_Tests.cs create mode 100644 framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/PersonEto.cs diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj b/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj index e4945cfc34..85c016fc25 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo.Abp.Ddd.Domain.csproj @@ -19,6 +19,7 @@ + diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs index 6f9c16ce88..fe4afd6e50 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/AbpDddDomainModule.cs @@ -4,6 +4,7 @@ using Volo.Abp.EventBus; using Volo.Abp.Guids; using Volo.Abp.Modularity; using Volo.Abp.MultiTenancy; +using Volo.Abp.ObjectMapping; using Volo.Abp.Threading; using Volo.Abp.Timing; using Volo.Abp.Uow; @@ -18,7 +19,9 @@ namespace Volo.Abp.Domain typeof(AbpMultiTenancyAbstractionsModule), typeof(AbpThreadingModule), typeof(AbpTimingModule), - typeof(AbpUnitOfWorkModule))] + typeof(AbpUnitOfWorkModule), + typeof(AbpObjectMappingModule) + )] public class AbpDddDomainModule : AbpModule { diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityCreatedEto.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityCreatedEto.cs new file mode 100644 index 0000000000..d39b69c857 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityCreatedEto.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.EventBus; + +namespace Volo.Abp.Domain.Entities.Events.Distributed +{ + [Serializable] + [GenericEventName(Postfix = ".Created")] + public class EntityCreatedEto + { + public TEntityEto Entity { get; set; } + + public EntityCreatedEto(TEntityEto entity) + { + Entity = entity; + } + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityDeletedEto.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityDeletedEto.cs new file mode 100644 index 0000000000..9cf930d7db --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityDeletedEto.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.EventBus; + +namespace Volo.Abp.Domain.Entities.Events.Distributed +{ + [Serializable] + [GenericEventName(Postfix = ".Deleted")] + public class EntityDeletedEto + { + public TEntityEto Entity { get; set; } + + public EntityDeletedEto(TEntityEto entity) + { + Entity = entity; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs new file mode 100644 index 0000000000..aa2a6e1c8f --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityEto.cs @@ -0,0 +1,20 @@ +using System; + +namespace Volo.Abp.Domain.Entities.Events.Distributed +{ + [Serializable] + public class EntityEto : EtoBase + { + public string KeysAsString { get; set; } + + public EntityEto() + { + + } + + public EntityEto(string keysAsString) + { + KeysAsString = keysAsString; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityToEtoMapper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityToEtoMapper.cs new file mode 100644 index 0000000000..354f72dddc --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityToEtoMapper.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.ObjectMapping; + +namespace Volo.Abp.Domain.Entities.Events.Distributed +{ + public class EntityToEtoMapper : IEntityToEtoMapper, ITransientDependency + { + protected IObjectMapper ObjectMapper { get; } + protected DistributedEventBusOptions Options { get; } + + public EntityToEtoMapper( + IOptions options, + IObjectMapper objectMapper) + { + ObjectMapper = objectMapper; + Options = options.Value; + } + + public object Map(object entityObj) + { + Check.NotNull(entityObj, nameof(entityObj)); + + var entity = entityObj as IEntity; + if (entity == null) + { + throw new ArgumentException($"{nameof(entityObj)} should be an entity and implement the '{typeof(IEntity).AssemblyQualifiedName}' interface!"); + } + + var entityType = ProxyHelper.UnProxy(entity).GetType(); + var etoType = Options.EtoMappings.GetOrDefault(entityType); + if (etoType == null) + { + var keys = entity.GetKeys().JoinAsString(","); + return new EntityEto(keys); + } + + //TODO: Also add KeysAsString property to resulting json for compability with the EntityEto! + return ObjectMapper.Map(entityType, etoType, entityObj); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityUpdatedEto.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityUpdatedEto.cs new file mode 100644 index 0000000000..654dd4243f --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EntityUpdatedEto.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.EventBus; + +namespace Volo.Abp.Domain.Entities.Events.Distributed +{ + [Serializable] + [GenericEventName(Postfix = ".Updated")] + public class EntityUpdatedEto + { + public TEntityEto Entity { get; set; } + + public EntityUpdatedEto(TEntityEto entity) + { + Entity = entity; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EtoBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EtoBase.cs new file mode 100644 index 0000000000..f574240dc8 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/EtoBase.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.Domain.Entities.Events.Distributed +{ + [Serializable] + public abstract class EtoBase + { + public Dictionary Properties { get; } + + protected EtoBase() + { + Properties = new Dictionary(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityToEtoMapper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityToEtoMapper.cs new file mode 100644 index 0000000000..e33e6751e8 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/IEntityToEtoMapper.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Domain.Entities.Events.Distributed +{ + public interface IEntityToEtoMapper + { + object Map(object entityObj); + } +} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/Temp.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/Temp.cs new file mode 100644 index 0000000000..0ac5b40a47 --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/Distributed/Temp.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.EventBus; +using Volo.Abp.EventBus.Distributed; + +namespace Volo.Abp.Domain.Entities.Events.Distributed +{ + /* + [EventName("Volo.Abp.Identity.Users.IdentityUser")] + public class UserEto : EtoBase + { + public Guid Id { get; set; } + + public string UserName { get; set; } + } + + //Event name (default): [Volo.Abp.Identity.Users.IdentityUser].Created/Updated/Deleted + + public class MyEventHandler : IDistributedEventHandler> + { + //Event name: Volo.Abp.Identity.Users.IdentityUser.Created + public Task HandleEventAsync(EntityCreatedEventInfo eventData) + { + throw new NotImplementedException(); + } + } + + */ +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs index c557013a03..6d5c03d4e1 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/EntityChangeEventHelper.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Auditing; using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.DynamicProxy; using Volo.Abp.EventBus; +using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Local; using Volo.Abp.Uow; @@ -16,70 +18,65 @@ namespace Volo.Abp.Domain.Entities.Events public class EntityChangeEventHelper : IEntityChangeEventHelper, ITransientDependency { public ILocalEventBus LocalEventBus { get; set; } + public IDistributedEventBus DistributedEventBus { get; set; } - private readonly IUnitOfWorkManager _unitOfWorkManager; + protected IUnitOfWorkManager UnitOfWorkManager { get; } + protected IEntityToEtoMapper EntityToEtoMapper { get; } - public EntityChangeEventHelper(IUnitOfWorkManager unitOfWorkManager) + public EntityChangeEventHelper( + IUnitOfWorkManager unitOfWorkManager, + IEntityToEtoMapper entityToEtoMapper) { - _unitOfWorkManager = unitOfWorkManager; + UnitOfWorkManager = unitOfWorkManager; + EntityToEtoMapper = entityToEtoMapper; + LocalEventBus = NullLocalEventBus.Instance; + DistributedEventBus = NullDistributedEventBus.Instance; } public async Task TriggerEventsAsync(EntityChangeReport changeReport) { await TriggerEventsInternalAsync(changeReport); - if (changeReport.IsEmpty() || _unitOfWorkManager.Current == null) + if (changeReport.IsEmpty() || UnitOfWorkManager.Current == null) { return; } - await _unitOfWorkManager.Current.SaveChangesAsync(); + await UnitOfWorkManager.Current.SaveChangesAsync(); } public virtual async Task TriggerEntityCreatingEventAsync(object entity) { - await TriggerEventWithEntity(typeof(EntityCreatingEventData<>), entity, true); - } - - public async Task TriggerEntityCreatedEventAsync(object entity) - { - await TriggerEventWithEntity(typeof(EntityCreatedEventData<>), entity, true); + await TriggerEventWithEntity(LocalEventBus, typeof(EntityCreatingEventData<>), entity, true); } public virtual async Task TriggerEntityCreatedEventOnUowCompletedAsync(object entity) { - await TriggerEventWithEntity(typeof(EntityCreatedEventData<>), entity, false); + await TriggerEventWithEntity(LocalEventBus, typeof(EntityCreatedEventData<>), entity, false); + await TriggerEventWithEntity(DistributedEventBus, typeof(EntityCreatedEto<>), EntityToEtoMapper.Map(entity), false); } public virtual async Task TriggerEntityUpdatingEventAsync(object entity) { - await TriggerEventWithEntity(typeof(EntityUpdatingEventData<>), entity, true); - } - - public async Task TriggerEntityUpdatedEventAsync(object entity) - { - await TriggerEventWithEntity(typeof(EntityUpdatedEventData<>), entity, true); + await TriggerEventWithEntity(LocalEventBus, typeof(EntityUpdatingEventData<>), entity, true); } public virtual async Task TriggerEntityUpdatedEventOnUowCompletedAsync(object entity) { - await TriggerEventWithEntity(typeof(EntityUpdatedEventData<>), entity, false); + await TriggerEventWithEntity(LocalEventBus, typeof(EntityUpdatedEventData<>), entity, false); + await TriggerEventWithEntity(DistributedEventBus, typeof(EntityUpdatedEto<>), EntityToEtoMapper.Map(entity), false); } public virtual async Task TriggerEntityDeletingEventAsync(object entity) { - await TriggerEventWithEntity(typeof(EntityDeletingEventData<>), entity, true); - } - - public async Task TriggerEntityDeletedEventAsync(object entity) - { - await TriggerEventWithEntity(typeof(EntityDeletedEventData<>), entity, true); + await TriggerEventWithEntity(LocalEventBus, typeof(EntityDeletingEventData<>), entity, true); } public virtual async Task TriggerEntityDeletedEventOnUowCompletedAsync(object entity) { - await TriggerEventWithEntity(typeof(EntityDeletedEventData<>), entity, false); + await TriggerEventWithEntity(LocalEventBus, typeof(EntityDeletedEventData<>), entity, false); + await TriggerEventWithEntity(DistributedEventBus, typeof(EntityDeletedEto<>), EntityToEtoMapper.Map(entity), false); } protected virtual async Task TriggerEventsInternalAsync(EntityChangeReport changeReport) @@ -120,18 +117,18 @@ namespace Volo.Abp.Domain.Entities.Events } } - protected virtual async Task TriggerEventWithEntity(Type genericEventType, object entity, bool triggerInCurrentUnitOfWork) + protected virtual async Task TriggerEventWithEntity(IEventPublisher eventPublisher, Type genericEventType, object entity, bool triggerInCurrentUnitOfWork) { var entityType = ProxyHelper.UnProxy(entity).GetType(); var eventType = genericEventType.MakeGenericType(entityType); - if (triggerInCurrentUnitOfWork || _unitOfWorkManager.Current == null) + if (triggerInCurrentUnitOfWork || UnitOfWorkManager.Current == null) { - await LocalEventBus.PublishAsync(eventType, Activator.CreateInstance(eventType, entity)); + await eventPublisher.PublishAsync(eventType, Activator.CreateInstance(eventType, entity)); return; } - _unitOfWorkManager.Current.OnCompleted(() => LocalEventBus.PublishAsync(eventType, Activator.CreateInstance(eventType, entity))); + UnitOfWorkManager.Current.OnCompleted(() => eventPublisher.PublishAsync(eventType, Activator.CreateInstance(eventType, entity))); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/IEntityChangeEventHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/IEntityChangeEventHelper.cs index acfcfb9875..59fa72ba09 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/IEntityChangeEventHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/Events/IEntityChangeEventHelper.cs @@ -10,15 +10,12 @@ namespace Volo.Abp.Domain.Entities.Events Task TriggerEventsAsync(EntityChangeReport changeReport); Task TriggerEntityCreatingEventAsync(object entity); - Task TriggerEntityCreatedEventAsync(object entity); Task TriggerEntityCreatedEventOnUowCompletedAsync(object entity); Task TriggerEntityUpdatingEventAsync(object entity); - Task TriggerEntityUpdatedEventAsync(object entity); Task TriggerEntityUpdatedEventOnUowCompletedAsync(object entity); Task TriggerEntityDeletingEventAsync(object entity); - Task TriggerEntityDeletedEventAsync(object entity); Task TriggerEntityDeletedEventOnUowCompletedAsync(object entity); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/EtoMappingDictionary.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/EtoMappingDictionary.cs new file mode 100644 index 0000000000..b9bcd3ed8b --- /dev/null +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/EtoMappingDictionary.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; + +namespace Volo.Abp.EventBus.Distributed +{ + public class EtoMappingDictionary : Dictionary + { + public void Add() + { + this[typeof(TEntity)] = typeof(TEntityEto); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalEventBusOptions.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalEventBusOptions.cs index 9e48bfa158..045efe9d02 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalEventBusOptions.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/LocalEventBusOptions.cs @@ -5,10 +5,12 @@ namespace Volo.Abp.EventBus.Distributed public class DistributedEventBusOptions { public ITypeList Handlers { get; } + public EtoMappingDictionary EtoMappings { get; set; } public DistributedEventBusOptions() { Handlers = new TypeList(); + EtoMappings = new EtoMappingDictionary(); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/NullLocalEventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/NullLocalEventBus.cs new file mode 100644 index 0000000000..cc59b9a24b --- /dev/null +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Distributed/NullLocalEventBus.cs @@ -0,0 +1,90 @@ +using System; +using System.Threading.Tasks; + +namespace Volo.Abp.EventBus.Distributed +{ + public sealed class NullDistributedEventBus : IDistributedEventBus + { + public static NullDistributedEventBus Instance { get; } = new NullDistributedEventBus(); + + private NullDistributedEventBus() + { + + } + + public IDisposable Subscribe(Func action) where TEvent : class + { + return NullDisposable.Instance; + } + + public IDisposable Subscribe(IEventHandler handler) where TEvent : class + { + return NullDisposable.Instance; + } + + public IDisposable Subscribe() where TEvent : class where THandler : IEventHandler, new() + { + return NullDisposable.Instance; + } + + public IDisposable Subscribe(Type eventType, IEventHandler handler) + { + return NullDisposable.Instance; + } + + public IDisposable Subscribe(IEventHandlerFactory factory) where TEvent : class + { + return NullDisposable.Instance; + } + + public IDisposable Subscribe(Type eventType, IEventHandlerFactory factory) + { + return NullDisposable.Instance; + } + + public void Unsubscribe(Func action) where TEvent : class + { + + } + + public void Unsubscribe(IEventHandler handler) where TEvent : class + { + + } + + public void Unsubscribe(Type eventType, IEventHandler handler) + { + + } + + public void Unsubscribe(IEventHandlerFactory factory) where TEvent : class + { + + } + + public void Unsubscribe(Type eventType, IEventHandlerFactory factory) + { + + } + + public void UnsubscribeAll() where TEvent : class + { + + } + + public void UnsubscribeAll(Type eventType) + { + + } + + public Task PublishAsync(TEvent eventData) where TEvent : class + { + return Task.CompletedTask; + } + + public Task PublishAsync(Type eventType, object eventData) + { + return Task.CompletedTask; + } + } +} diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/GenericEventNameAttribute.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/GenericEventNameAttribute.cs index 638a47af0a..8e44905c42 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/GenericEventNameAttribute.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/GenericEventNameAttribute.cs @@ -5,7 +5,11 @@ namespace Volo.Abp.EventBus [AttributeUsage(AttributeTargets.Class)] public class GenericEventNameAttribute : Attribute, IEventNameProvider { - public string GetName(Type eventType) + public string Prefix { get; set; } + + public string Postfix { get; set; } + + public virtual string GetName(Type eventType) { if (!eventType.IsGenericType) { @@ -18,7 +22,19 @@ namespace Volo.Abp.EventBus throw new AbpException($"Given type has more than one generic argument: {eventType.AssemblyQualifiedName}"); } - return EventNameAttribute.GetNameOrDefault(genericArguments[0]); + var eventName = EventNameAttribute.GetNameOrDefault(genericArguments[0]); + + if (!Prefix.IsNullOrEmpty()) + { + eventName = Prefix + eventName; + } + + if (!Postfix.IsNullOrEmpty()) + { + eventName = eventName + Postfix; + } + + return eventName; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/NullLocalEventBus.cs b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/NullLocalEventBus.cs similarity index 97% rename from framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/NullLocalEventBus.cs rename to framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/NullLocalEventBus.cs index f02365d8fa..494db3edd1 100644 --- a/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/NullLocalEventBus.cs +++ b/framework/src/Volo.Abp.EventBus/Volo/Abp/EventBus/Local/NullLocalEventBus.cs @@ -1,8 +1,7 @@ using System; using System.Threading.Tasks; -using Volo.Abp.EventBus.Local; -namespace Volo.Abp.EventBus +namespace Volo.Abp.EventBus.Local { public sealed class NullLocalEventBus : ILocalEventBus { diff --git a/framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/ObjectMapperExtensions.cs b/framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/ObjectMapperExtensions.cs new file mode 100644 index 0000000000..eca130638f --- /dev/null +++ b/framework/src/Volo.Abp.ObjectMapping/Volo/Abp/ObjectMapping/ObjectMapperExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Reflection; + +namespace Volo.Abp.ObjectMapping +{ + public static class ObjectMapperExtensions + { + private static readonly MethodInfo MapToNewObjectMethod; + private static readonly MethodInfo MapToExistingObjectMethod; + + static ObjectMapperExtensions() + { + var methods = typeof(IObjectMapper).GetMethods(); + foreach (var method in methods) + { + if (method.Name == nameof(IObjectMapper.Map) && method.IsGenericMethodDefinition) + { + var parameters = method.GetParameters(); + if (parameters.Length == 1) + { + MapToNewObjectMethod = method; + } + else if (parameters.Length == 2) + { + MapToExistingObjectMethod = method; + } + } + } + } + + public static object Map(this IObjectMapper objectMapper, Type sourceType, Type destinationType, object source) + { + return MapToNewObjectMethod + .MakeGenericMethod(sourceType, destinationType) + .Invoke(objectMapper, new[] { source }); + } + + public static object Map(this IObjectMapper objectMapper, Type sourceType, Type destinationType, object source, object destination) + { + return MapToExistingObjectMethod + .MakeGenericMethod(sourceType, destinationType) + .Invoke(objectMapper, new[] { source, destination }); + } + } +} diff --git a/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/ObjectMapperExtensions_Tests.cs b/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/ObjectMapperExtensions_Tests.cs new file mode 100644 index 0000000000..3958041817 --- /dev/null +++ b/framework/test/Volo.Abp.AutoMapper.Tests/Volo/Abp/AutoMapper/ObjectMapperExtensions_Tests.cs @@ -0,0 +1,26 @@ +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using System; +using Volo.Abp.AutoMapper.SampleClasses; +using Volo.Abp.ObjectMapping; +using Xunit; + +namespace Volo.Abp.AutoMapper +{ + public class ObjectMapperExtensions_Tests : AbpIntegratedTest + { + private readonly IObjectMapper _objectMapper; + + public ObjectMapperExtensions_Tests() + { + _objectMapper = ServiceProvider.GetRequiredService(); + } + + [Fact] + public void Should_Map_Objects_With_AutoMap_Attributes() + { + var dto = _objectMapper.Map(typeof(MyEntity), typeof(MyEntityDto), new MyEntity { Number = 42 }); + dto.As().Number.ShouldBe(42); + } + } +} diff --git a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/GenericEventNameAttribute_Tests.cs b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/GenericEventNameAttribute_Tests.cs index 640801ad58..20a9728e7f 100644 --- a/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/GenericEventNameAttribute_Tests.cs +++ b/framework/test/Volo.Abp.EventBus.Tests/Volo/Abp/EventBus/GenericEventNameAttribute_Tests.cs @@ -1,4 +1,5 @@ -using Shouldly; +using System.Linq; +using Shouldly; using Xunit; namespace Volo.Abp.EventBus @@ -8,16 +9,25 @@ namespace Volo.Abp.EventBus [Fact] public void Should_Properly_Get_EventName() { - new GenericEventNameAttribute() - .GetName(typeof(MyGenericType)) - .ShouldBe(typeof(MyInnerType).FullName); + var eventType = typeof(MyGenericType); + + var eventNameProvider = eventType + .GetCustomAttributes(true) + .OfType() + .Single(); + + eventNameProvider + .GetName(eventType) + .ShouldBe("MyEvent.GenericTest"); } + [EventName("MyEvent")] public class MyInnerType { } + [GenericEventName(Postfix = ".GenericTest")] public class MyGenericType { diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs index 068509cc19..3600ceaeeb 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs @@ -1,10 +1,12 @@ using System; using System.Collections.ObjectModel; +using Volo.Abp.AutoMapper; using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; namespace Volo.Abp.TestApp.Domain { + [AutoMapTo(typeof(PersonEto))] public class Person : FullAuditedAggregateRoot, IMultiTenant { public virtual Guid? TenantId { get; set; } diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/PersonEto.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/PersonEto.cs new file mode 100644 index 0000000000..288911da19 --- /dev/null +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/PersonEto.cs @@ -0,0 +1,17 @@ +using System; +using Volo.Abp.Domain.Entities.Events.Distributed; + +namespace Volo.Abp.TestApp.Domain +{ + //[Serializable] //TODO: ??? + public class PersonEto : EntityEto + { + public virtual Guid? TenantId { get; set; } + + public virtual Guid? CityId { get; set; } + + public virtual string Name { get; set; } + + public virtual int Age { get; set; } + } +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestAppModule.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestAppModule.cs index 4de2e405e1..747218984f 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestAppModule.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestAppModule.cs @@ -4,6 +4,7 @@ using Volo.Abp.Autofac; using Volo.Abp.Modularity; using Volo.Abp.TestApp.Domain; using Volo.Abp.AutoMapper; +using Volo.Abp.EventBus.Distributed; using Volo.Abp.TestApp.Application.Dto; namespace Volo.Abp.TestApp @@ -18,7 +19,8 @@ namespace Volo.Abp.TestApp { public override void ConfigureServices(ServiceConfigurationContext context) { - ConfigureAutoMapper(context.Services); + ConfigureAutoMapper(); + ConfigureDistributedEventBus(); } public override void OnApplicationInitialization(ApplicationInitializationContext context) @@ -26,9 +28,9 @@ namespace Volo.Abp.TestApp SeedTestData(context); } - private static void ConfigureAutoMapper(IServiceCollection services) + private void ConfigureAutoMapper() { - services.Configure(options => + Configure(options => { options.Configurators.Add(ctx => { @@ -38,6 +40,14 @@ namespace Volo.Abp.TestApp }); } + private void ConfigureDistributedEventBus() + { + Configure(options => + { + options.EtoMappings.Add(); + }); + } + private static void SeedTestData(ApplicationInitializationContext context) { using (var scope = context.ServiceProvider.CreateScope()) diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs index 0fd6a72a04..67032507f3 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/EntityChangeEvents_Tests.cs @@ -2,8 +2,9 @@ using System; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.Domain.Entities.Events.Distributed; using Volo.Abp.Domain.Repositories; -using Volo.Abp.EventBus; +using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Local; using Volo.Abp.Modularity; using Volo.Abp.TestApp.Domain; @@ -17,11 +18,13 @@ namespace Volo.Abp.TestApp.Testing { protected IRepository PersonRepository { get; } protected ILocalEventBus LocalEventBus { get; } + protected IDistributedEventBus DistributedEventBus { get; } protected EntityChangeEvents_Tests() { PersonRepository = GetRequiredService>(); LocalEventBus = GetRequiredService(); + DistributedEventBus = GetRequiredService(); } [Fact] @@ -31,6 +34,8 @@ namespace Volo.Abp.TestApp.Testing var creatingEventTriggered = false; var createdEventTriggered = false; + var createdEtoTriggered = false; + var updatedEtoTriggered = false; var updatingEventTriggered = false; var updatedEventTriggered = false; @@ -70,6 +75,15 @@ namespace Volo.Abp.TestApp.Testing return Task.CompletedTask; }); + DistributedEventBus.Subscribe>(eto => + { + eto.Entity.Name.ShouldBe(personName); + + createdEtoTriggered = true; + + return Task.CompletedTask; + }); + LocalEventBus.Subscribe>(data => { creatingEventTriggered.ShouldBeTrue(); @@ -100,6 +114,16 @@ namespace Volo.Abp.TestApp.Testing return Task.CompletedTask; }); + DistributedEventBus.Subscribe>(eto => + { + eto.Entity.Name.ShouldBe(personName); + eto.Entity.Age.ShouldBe(18); + + updatedEtoTriggered = true; + + return Task.CompletedTask; + }); + PersonRepository.Insert(new Person(Guid.NewGuid(), personName, 15)); uow.Complete(); @@ -107,8 +131,10 @@ namespace Volo.Abp.TestApp.Testing creatingEventTriggered.ShouldBeTrue(); createdEventTriggered.ShouldBeTrue(); + createdEtoTriggered.ShouldBeTrue(); updatingEventTriggered.ShouldBeTrue(); updatedEventTriggered.ShouldBeTrue(); + updatedEtoTriggered.ShouldBeTrue(); } } }