diff --git a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs index 2457c3c453..1dc38bf5ea 100644 --- a/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs +++ b/framework/src/Volo.Abp.MemoryDb/Volo/Abp/Domain/Repositories/MemoryDb/MemoryDbRepository.cs @@ -4,7 +4,12 @@ using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; +using Volo.Abp.Auditing; using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Entities.Events; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.EventBus.Local; +using Volo.Abp.Guids; using Volo.Abp.MemoryDb; namespace Volo.Abp.Domain.Repositories.MemoryDb @@ -20,62 +25,224 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb public virtual IMemoryDatabase Database => DatabaseProvider.GetDatabase(); protected IMemoryDatabaseProvider DatabaseProvider { get; } + + public ILocalEventBus LocalEventBus { get; set; } + + public IDistributedEventBus DistributedEventBus { get; set; } + + public IEntityChangeEventHelper EntityChangeEventHelper { get; set; } + + public IAuditPropertySetter AuditPropertySetter { get; set; } + + public IGuidGenerator GuidGenerator { get; set; } public MemoryDbRepository(IMemoryDatabaseProvider databaseProvider) { DatabaseProvider = databaseProvider; + + LocalEventBus = NullLocalEventBus.Instance; + DistributedEventBus = NullDistributedEventBus.Instance; + EntityChangeEventHelper = NullEntityChangeEventHelper.Instance; } protected override IQueryable GetQueryable() { return ApplyDataFilters(Collection.AsQueryable()); } + + protected virtual async Task TriggerDomainEventsAsync(object entity) + { + var generatesDomainEventsEntity = entity as IGeneratesDomainEvents; + if (generatesDomainEventsEntity == null) + { + return; + } + + var localEvents = generatesDomainEventsEntity.GetLocalEvents()?.ToArray(); + if (localEvents != null && localEvents.Any()) + { + foreach (var localEvent in localEvents) + { + await LocalEventBus.PublishAsync(localEvent.GetType(), localEvent); + } + + generatesDomainEventsEntity.ClearLocalEvents(); + } + + var distributedEvents = generatesDomainEventsEntity.GetDistributedEvents()?.ToArray(); + if (distributedEvents != null && distributedEvents.Any()) + { + foreach (var distributedEvent in distributedEvents) + { + await DistributedEventBus.PublishAsync(distributedEvent.GetType(), distributedEvent); + } + + generatesDomainEventsEntity.ClearDistributedEvents(); + } + } + + protected virtual bool IsHardDeleted(TEntity entity) + { + if (!(UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) is HashSet hardDeletedEntities)) + { + return false; + } + + return hardDeletedEntities.Contains(entity); + } + + protected virtual void CheckAndSetId(TEntity entity) + { + if (entity is IEntity entityWithGuidId) + { + TrySetGuidId(entityWithGuidId); + } + } + + protected virtual void TrySetGuidId(IEntity entity) + { + if (entity.Id != default) + { + return; + } + + EntityHelper.TrySetId( + entity, + () => GuidGenerator.Create(), + true + ); + } + + protected virtual void SetCreationAuditProperties(TEntity entity) + { + AuditPropertySetter.SetCreationProperties(entity); + } + + protected virtual void SetModificationAuditProperties(TEntity entity) + { + AuditPropertySetter.SetModificationProperties(entity); + } + + protected virtual void SetDeletionAuditProperties(TEntity entity) + { + AuditPropertySetter.SetDeletionProperties(entity); + } + + protected virtual async Task TriggerEntityCreateEvents(TEntity entity) + { + await EntityChangeEventHelper.TriggerEntityCreatedEventOnUowCompletedAsync(entity); + await EntityChangeEventHelper.TriggerEntityCreatingEventAsync(entity); + } + + protected virtual async Task TriggerEntityUpdateEventsAsync(TEntity entity) + { + await EntityChangeEventHelper.TriggerEntityUpdatedEventOnUowCompletedAsync(entity); + await EntityChangeEventHelper.TriggerEntityUpdatingEventAsync(entity); + } + + protected virtual async Task TriggerEntityDeleteEventsAsync(TEntity entity) + { + await EntityChangeEventHelper.TriggerEntityDeletedEventOnUowCompletedAsync(entity); + await EntityChangeEventHelper.TriggerEntityDeletingEventAsync(entity); + } + + protected virtual async Task ApplyAbpConceptsForAddedEntityAsync(TEntity entity) + { + CheckAndSetId(entity); + SetCreationAuditProperties(entity); + await TriggerEntityCreateEvents(entity); + await TriggerDomainEventsAsync(entity); + } + + protected virtual async Task ApplyAbpConceptsForDeletedEntityAsync(TEntity entity) + { + SetDeletionAuditProperties(entity); + await TriggerEntityDeleteEventsAsync(entity); + await TriggerDomainEventsAsync(entity); + } public override Task FindAsync( Expression> predicate, bool includeDetails = true, CancellationToken cancellationToken = default) { - return Task.FromResult(Collection.AsQueryable().Where(predicate).SingleOrDefault()); + return Task.FromResult(GetQueryable().Where(predicate).SingleOrDefault()); } - public override Task DeleteAsync(Expression> predicate, bool autoSave = false, CancellationToken cancellationToken = default) + public override async Task DeleteAsync( + Expression> predicate, + bool autoSave = false, + CancellationToken cancellationToken = default) { - var entities = Collection.AsQueryable().Where(predicate).ToList(); + var entities = GetQueryable().Where(predicate).ToList(); foreach (var entity in entities) { - Collection.Remove(entity); + await DeleteAsync(entity, autoSave, cancellationToken); } - - return Task.CompletedTask; } - public override Task InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) + public override async Task InsertAsync( + TEntity entity, + bool autoSave = false, + CancellationToken cancellationToken = default) { + await ApplyAbpConceptsForAddedEntityAsync(entity); + Collection.Add(entity); - return Task.FromResult(entity); + + return entity; } - - public override Task UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) + + public override async Task UpdateAsync( + TEntity entity, + bool autoSave = false, + CancellationToken cancellationToken = default) { + SetModificationAuditProperties(entity); + + if (entity is ISoftDelete softDeleteEntity && softDeleteEntity.IsDeleted) + { + SetDeletionAuditProperties(entity); + await TriggerEntityDeleteEventsAsync(entity); + } + else + { + await TriggerEntityUpdateEventsAsync(entity); + } + + await TriggerDomainEventsAsync(entity); + Collection.Update(entity); - return Task.FromResult(entity); - } - public override Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) + return entity; + } + + public override async Task DeleteAsync( + TEntity entity, + bool autoSave = false, + CancellationToken cancellationToken = default) { - Collection.Remove(entity); - return Task.CompletedTask; + await ApplyAbpConceptsForDeletedEntityAsync(entity); + + if (entity is ISoftDelete softDeleteEntity && !IsHardDeleted(entity)) + { + softDeleteEntity.IsDeleted = true; + Collection.Update(entity); + } + else + { + Collection.Remove(entity); + } } public override Task> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default) { - return Task.FromResult(Collection.ToList()); + return Task.FromResult(GetQueryable().ToList()); } public override Task GetCountAsync(CancellationToken cancellationToken = default) { - return Task.FromResult(Collection.LongCount()); + return Task.FromResult(GetQueryable().LongCount()); } } @@ -126,13 +293,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default) { - var entity = await FindAsync(id, cancellationToken: cancellationToken); - if (entity == null) - { - return; - } - - await DeleteAsync(entity, autoSave, cancellationToken); + await DeleteAsync(x => x.Id.Equals(id), autoSave, cancellationToken); } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Auditing/Auditing_Tests.cs new file mode 100644 index 0000000000..3b0e1aa8f7 --- /dev/null +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Auditing/Auditing_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MemoryDb.Auditing +{ + public class Auditing_Tests : Auditing_Tests + { + + } +} diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DataFilters/SoftDelete_Tests.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DataFilters/SoftDelete_Tests.cs new file mode 100644 index 0000000000..dea03664b0 --- /dev/null +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DataFilters/SoftDelete_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MemoryDb.DataFilters +{ + public class SoftDelete_Tests : SoftDelete_Tests + { + + } +} diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/DomainEvents_Tests.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/DomainEvents_Tests.cs new file mode 100644 index 0000000000..52af309369 --- /dev/null +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/DomainEvents_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MemoryDb.DomainEvents +{ + public class DomainEvents_Tests : DomainEvents_Tests + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/EntityChangeEvents_Tests.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/EntityChangeEvents_Tests.cs new file mode 100644 index 0000000000..be5c014765 --- /dev/null +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/DomainEvents/EntityChangeEvents_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MemoryDb.DomainEvents +{ + public class EntityChangeEvents_Tests : EntityChangeEvents_Tests + { + + } +}