From fc8d34eae94bb628ea170321fe8236fa4eb73b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 14 Feb 2020 19:33:29 +0300 Subject: [PATCH] Refactor and improvements for #2807 --- .../Volo/Abp/Domain/Entities/EntityHelper.cs | 21 ------- .../Abp/Domain/Repositories/RepositoryBase.cs | 1 + .../Repositories/RepositoryExtensions.cs | 61 +++++++++++++------ .../UnitOfWorkExtensionDataTypes.cs | 11 ---- .../Repositories/UnitOfWorkItemNames.cs | 7 +++ .../EntityFrameworkCore/EfCoreRepository.cs | 4 +- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 40 ++++++------ .../Repositories/MongoDB/MongoDbRepository.cs | 23 ++----- .../Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs | 1 + .../Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs | 8 +-- .../Abp/TestApp/Testing/HardDelete_Tests.cs | 55 ++++++++--------- 11 files changed, 105 insertions(+), 127 deletions(-) delete mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs create mode 100644 framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkItemNames.cs diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs index cce1e9c18f..a33c5f56ec 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs @@ -112,26 +112,5 @@ namespace Volo.Abp.Domain.Entities idProperty.SetValue(entity, idFactory()); } - - public static object GetEntityId(object entity) - { - if (!IsEntity(entity.GetType())) - { - throw new AbpException(entity.GetType() + " is not an Entity !"); - } - - return ReflectionHelper.GetValueByPath(entity, entity.GetType(), "Id"); - } - public static string GetHardDeleteKey(object entity, string tenantId) - { - //if (entity is IMultiTenant) // IsMultiTenantEntity - if (typeof(IMultiTenant).IsAssignableFrom(entity.GetType())) - { - var tenantIdString = !string.IsNullOrEmpty(tenantId) ? tenantId : "null"; - return entity.GetType().FullName + ";TenantId=" + tenantIdString + ";Id=" + GetEntityId(entity); - } - - return entity.GetType().FullName + ";Id=" + GetEntityId(entity); - } } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs index b908827a98..3ade403017 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs @@ -18,6 +18,7 @@ namespace Volo.Abp.Domain.Repositories public IDataFilter DataFilter { get; set; } public ICurrentTenant CurrentTenant { get; set; } + public IUnitOfWorkManager UnitOfWorkManager { get; set; } public virtual Type ElementType => GetQueryable().ElementType; diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs index 9a2bc147af..ef44d6830d 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; @@ -44,31 +43,55 @@ namespace Volo.Abp.Domain.Repositories } } - public static async Task HardDeleteAsync(this IRepository repository, TEntity entity) - where TEntity : class, IEntity, ISoftDelete + public static async Task HardDeleteAsync( + this IBasicRepository repository, + TEntity entity, + bool autoSave = false, + CancellationToken cancellationToken = default + ) + where TEntity : class, IEntity, ISoftDelete { - var repo = ProxyHelper.UnProxy(repository) as IRepository; - if (repo != null) + if (!(ProxyHelper.UnProxy(repository) is IUnitOfWorkManagerAccessor unitOfWorkManagerAccessor)) { - var uow = ((IUnitOfWorkManagerAccessor)repo).UnitOfWorkManager; - var baseRepository = ((RepositoryBase)repo); - - var items = ((IUnitOfWorkManagerAccessor)repo).UnitOfWorkManager.Current.Items; - var hardDeleteEntities = items.GetOrAdd(UnitOfWorkExtensionDataTypes.HardDelete, () => new HashSet()) as HashSet; + throw new AbpException($"The given repository (of type {repository.GetType().AssemblyQualifiedName}) should implement the {typeof(IUnitOfWorkManagerAccessor).AssemblyQualifiedName} interface in order to invoke the {nameof(HardDeleteAsync)} method!"); + } - var hardDeleteKey = EntityHelper.GetHardDeleteKey(entity, baseRepository.CurrentTenant?.Id?.ToString()); - hardDeleteEntities.Add(hardDeleteKey); + var uowManager = unitOfWorkManagerAccessor.UnitOfWorkManager; + if (uowManager == null) + { + throw new AbpException($"{nameof(unitOfWorkManagerAccessor.UnitOfWorkManager)} property of the given {nameof(repository)} object is null!"); + } - await repo.DeleteAsync(entity); + if (uowManager.Current == null) + { + using (var uow = uowManager.Begin()) + { + await HardDeleteWithUnitOfWorkAsync(repository, entity, autoSave, cancellationToken, uowManager.Current); + await uow.CompleteAsync(cancellationToken); + } } - } - public static async Task HardDeleteAsync(this IRepository repository, Expression> predicate) - where TEntity : class, IEntity, ISoftDelete - { - foreach (var entity in repository.Where(predicate).ToList()) + else { - await repository.HardDeleteAsync(entity); + await HardDeleteWithUnitOfWorkAsync(repository, entity, autoSave, cancellationToken, uowManager.Current); } } + + private static async Task HardDeleteWithUnitOfWorkAsync( + IBasicRepository repository, + TEntity entity, + bool autoSave, + CancellationToken cancellationToken, IUnitOfWork currentUow + ) + where TEntity : class, IEntity, ISoftDelete + { + var hardDeleteEntities = (HashSet) currentUow.Items.GetOrAdd( + UnitOfWorkItemNames.HardDeletedEntities, + () => new HashSet() + ); + + hardDeleteEntities.Add(entity); + + await repository.DeleteAsync(entity, autoSave, cancellationToken); + } } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs deleted file mode 100644 index a95aca42c0..0000000000 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Volo.Abp.Domain.Repositories -{ - public class UnitOfWorkExtensionDataTypes - { - public static string HardDelete { get; } = "HardDelete"; - } -} diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkItemNames.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkItemNames.cs new file mode 100644 index 0000000000..4ec067ceec --- /dev/null +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkItemNames.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Domain.Repositories +{ + public static class UnitOfWorkItemNames + { + public const string HardDeletedEntities = "AbpHardDeletedEntities"; + } +} diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs index 6c3eb689d8..106e00f77a 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs @@ -80,7 +80,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore { return includeDetails ? await WithDetails().ToListAsync(GetCancellationToken(cancellationToken)) - : await DbSet.ToListAsync(GetCancellationToken(cancellationToken)); + : await DbSet.ToListAsync(GetCancellationToken(cancellationToken)); } public override async Task GetCountAsync(CancellationToken cancellationToken = default) @@ -208,7 +208,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore { return includeDetails ? await WithDetails().FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken)) - : await DbSet.FindAsync(new object[] { id }, GetCancellationToken(cancellationToken)); + : await DbSet.FindAsync(new object[] {id}, GetCancellationToken(cancellationToken)); } public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 4e2f4c0d51..ba9cee70ee 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -50,6 +50,7 @@ namespace Volo.Abp.EntityFrameworkCore public IEntityHistoryHelper EntityHistoryHelper { get; set; } public IAuditingManager AuditingManager { get; set; } + public IUnitOfWorkManager UnitOfWorkManager { get; set; } public IClock Clock { get; set; } @@ -199,37 +200,24 @@ namespace Volo.Abp.EntityFrameworkCore protected virtual void ApplyAbpConceptsForDeletedEntity(EntityEntry entry, EntityChangeReport changeReport) { - if (IsHardDeleteEntity(entry)) + if (TryCancelDeletionForSoftDelete(entry)) { - changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted)); - return; + UpdateConcurrencyStamp(entry); + SetDeletionAuditProperties(entry); } - CancelDeletionForSoftDelete(entry); - UpdateConcurrencyStamp(entry); - SetDeletionAuditProperties(entry); + changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted)); } - protected virtual bool IsHardDeleteEntity(EntityEntry entry) + protected virtual bool IsHardDeleted(EntityEntry entry) { - if (UnitOfWorkManager?.Current?.Items == null) - { - return false; - } - - if (!UnitOfWorkManager.Current.Items.ContainsKey(UnitOfWorkExtensionDataTypes.HardDelete)) + var hardDeletedEntities = UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) as HashSet; + if (hardDeletedEntities == null) { return false; } - var hardDeleteItems = UnitOfWorkManager.Current.Items[UnitOfWorkExtensionDataTypes.HardDelete]; - if (!(hardDeleteItems is HashSet objects)) - { - return false; - } - string hardDeleteKey = EntityHelper.GetHardDeleteKey(entry.Entity, CurrentTenantId?.ToString()); - - return objects.Contains(hardDeleteKey); + return hardDeletedEntities.Contains(entry.Entity); } protected virtual void AddDomainEvents(EntityChangeReport changeReport, object entityAsObj) @@ -283,16 +271,22 @@ namespace Volo.Abp.EntityFrameworkCore entity.ConcurrencyStamp = Guid.NewGuid().ToString("N"); } - protected virtual void CancelDeletionForSoftDelete(EntityEntry entry) + protected virtual bool TryCancelDeletionForSoftDelete(EntityEntry entry) { if (!(entry.Entity is ISoftDelete)) { - return; + return false; + } + + if (IsHardDeleted(entry)) + { + return false; } entry.Reload(); entry.State = EntityState.Modified; entry.Entity.As().IsDeleted = true; + return true; } protected virtual void CheckAndSetId(EntityEntry entry) diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index b827292702..3333f1d505 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -111,7 +111,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB await ApplyAbpConceptsForDeletedEntityAsync(entity); var oldConcurrencyStamp = SetNewConcurrencyStamp(entity); - if (entity is ISoftDelete softDeleteEntity && !IsHardDeleteEntity(entity)) + if (entity is ISoftDelete softDeleteEntity && !IsHardDeleted(entity)) { softDeleteEntity.IsDeleted = true; var result = await Collection.ReplaceOneAsync( @@ -175,32 +175,21 @@ namespace Volo.Abp.Domain.Repositories.MongoDB Collection.AsQueryable() ); } - protected virtual bool IsHardDeleteEntity(TEntity entry) + protected virtual bool IsHardDeleted(TEntity entity) { - if (UnitOfWorkManager?.Current?.Items == null) + var hardDeletedEntities = UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) as HashSet; + if (hardDeletedEntities == null) { return false; } - if (!UnitOfWorkManager.Current.Items.ContainsKey(UnitOfWorkExtensionDataTypes.HardDelete)) - { - return false; - } - - var hardDeleteItems = UnitOfWorkManager.Current.Items[UnitOfWorkExtensionDataTypes.HardDelete]; - if (!(hardDeleteItems is HashSet objects)) - { - return false; - } - string hardDeleteKey = EntityHelper.GetHardDeleteKey(entry, CurrentTenant?.Id.ToString()); - - return objects.Contains(hardDeleteKey); + return hardDeletedEntities.Contains(entity); } protected virtual FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null) { throw new NotImplementedException( - $"{nameof(CreateEntityFilter)} is not implemented for MongoDB by default. It should be overrided and implemented by the deriving class!" + $"{nameof(CreateEntityFilter)} is not implemented for MongoDB by default. It should be overriden and implemented by the deriving class!" ); } diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs index 97fd81372b..ee5baa58bc 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs @@ -9,6 +9,7 @@ namespace Volo.Abp.Uow public interface IUnitOfWork : IDatabaseApiContainer, ITransactionApiContainer, IDisposable { Guid Id { get; } + Dictionary Items { get; } //TODO: Switch to OnFailed (sync) and OnDisposed (sync) methods to be compatible with OnCompleted diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs index c494bfdfe6..05f49df180 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -31,6 +32,7 @@ namespace Volo.Abp.Uow public IServiceProvider ServiceProvider { get; } + [NotNull] public Dictionary Items { get; } private readonly Dictionary _databaseApis; @@ -48,6 +50,7 @@ namespace Volo.Abp.Uow _databaseApis = new Dictionary(); _transactionApis = new Dictionary(); + Items = new Dictionary(); } @@ -320,10 +323,5 @@ namespace Volo.Abp.Uow { return $"[UnitOfWork {Id}]"; } - - public Dictionary GetHardDeleteItems() - { - return Items; - } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs index 9d0430e968..1eac79fd7c 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs @@ -1,7 +1,5 @@ using Shouldly; using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Volo.Abp.Data; using Volo.Abp.Domain.Repositories; @@ -15,58 +13,57 @@ namespace Volo.Abp.TestApp.Testing public abstract class HardDelete_Tests : TestAppTestBase where TStartupModule : IAbpModule { - protected readonly IRepository _personRepository; + protected readonly IRepository PersonRepository; protected readonly IDataFilter DataFilter; - protected readonly IUnitOfWorkManager _unitOfWorkManager; - public HardDelete_Tests() + protected readonly IUnitOfWorkManager UnitOfWorkManager; + + protected HardDelete_Tests() { - _personRepository = GetRequiredService>(); + PersonRepository = GetRequiredService>(); DataFilter = GetRequiredService(); - _unitOfWorkManager = GetRequiredService(); + UnitOfWorkManager = GetRequiredService(); } + [Fact] - public async Task Should_HardDelete_Entity_With_Collection() + public async Task Should_HardDelete_Entities() { - using (var uow = _unitOfWorkManager.Begin()) - { - using (DataFilter.Disable()) - { - var douglas = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); - await _personRepository.HardDeleteAsync(x => x.Id == TestDataBuilder.UserDouglasId); - await uow.CompleteAsync(); - } + var douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId); + await PersonRepository.HardDeleteAsync(douglas); - var deletedDougles = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); - deletedDougles.ShouldBeNull(); - } + douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); + douglas.ShouldBeNull(); } + [Fact] public async Task Should_HardDelete_Soft_Deleted_Entities() { - var douglas = await _personRepository.GetAsync(TestDataBuilder.UserDouglasId); - await _personRepository.DeleteAsync(douglas); + var douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId); + await PersonRepository.DeleteAsync(douglas); - douglas = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); + douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); douglas.ShouldBeNull(); using (DataFilter.Disable()) { - douglas = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); + douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); douglas.ShouldNotBeNull(); douglas.IsDeleted.ShouldBeTrue(); douglas.DeletionTime.ShouldNotBeNull(); } - using (var uow = _unitOfWorkManager.Begin()) + + using (var uow = UnitOfWorkManager.Begin()) { using (DataFilter.Disable()) { - douglas = await _personRepository.GetAsync(TestDataBuilder.UserDouglasId); - await _personRepository.HardDeleteAsync(douglas); - await uow.CompleteAsync(); - var deletedDougles = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); - deletedDougles.ShouldBeNull(); + douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId); } + + await PersonRepository.HardDeleteAsync(douglas); + await uow.CompleteAsync(); } + + douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId); + douglas.ShouldBeNull(); } } }