Browse Source

Refactor and improvements for #2807

pull/2844/head
Halil İbrahim Kalkan 6 years ago
parent
commit
fc8d34eae9
  1. 21
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs
  2. 1
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs
  3. 61
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs
  4. 11
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs
  5. 7
      framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkItemNames.cs
  6. 4
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/EfCoreRepository.cs
  7. 40
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  8. 23
      framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs
  9. 1
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/IUnitOfWork.cs
  10. 8
      framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWork.cs
  11. 55
      framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HardDelete_Tests.cs

21
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);
}
}
}

1
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;

61
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<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository, TEntity entity)
where TEntity : class, IEntity<TPrimaryKey>, ISoftDelete
public static async Task HardDeleteAsync<TEntity>(
this IBasicRepository<TEntity> repository,
TEntity entity,
bool autoSave = false,
CancellationToken cancellationToken = default
)
where TEntity : class, IEntity, ISoftDelete
{
var repo = ProxyHelper.UnProxy(repository) as IRepository<TEntity, TPrimaryKey>;
if (repo != null)
if (!(ProxyHelper.UnProxy(repository) is IUnitOfWorkManagerAccessor unitOfWorkManagerAccessor))
{
var uow = ((IUnitOfWorkManagerAccessor)repo).UnitOfWorkManager;
var baseRepository = ((RepositoryBase<TEntity>)repo);
var items = ((IUnitOfWorkManagerAccessor)repo).UnitOfWorkManager.Current.Items;
var hardDeleteEntities = items.GetOrAdd(UnitOfWorkExtensionDataTypes.HardDelete, () => new HashSet<string>()) as HashSet<string>;
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<TEntity, TPrimaryKey>(this IRepository<TEntity, TPrimaryKey> repository, Expression<Func<TEntity, bool>> predicate)
where TEntity : class, IEntity<TPrimaryKey>, 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<TEntity>(
IBasicRepository<TEntity> repository,
TEntity entity,
bool autoSave,
CancellationToken cancellationToken, IUnitOfWork currentUow
)
where TEntity : class, IEntity, ISoftDelete
{
var hardDeleteEntities = (HashSet<IEntity>) currentUow.Items.GetOrAdd(
UnitOfWorkItemNames.HardDeletedEntities,
() => new HashSet<IEntity>()
);
hardDeleteEntities.Add(entity);
await repository.DeleteAsync(entity, autoSave, cancellationToken);
}
}
}

11
framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/UnitOfWorkExtensionDataTypes.cs

@ -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";
}
}

7
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";
}
}

4
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<long> 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)

40
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<IEntity>;
if (hardDeletedEntities == null)
{
return false;
}
var hardDeleteItems = UnitOfWorkManager.Current.Items[UnitOfWorkExtensionDataTypes.HardDelete];
if (!(hardDeleteItems is HashSet<string> 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<ISoftDelete>().IsDeleted = true;
return true;
}
protected virtual void CheckAndSetId(EntityEntry entry)

23
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<IEntity>;
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<string> objects))
{
return false;
}
string hardDeleteKey = EntityHelper.GetHardDeleteKey(entry, CurrentTenant?.Id.ToString());
return objects.Contains(hardDeleteKey);
return hardDeletedEntities.Contains(entity);
}
protected virtual FilterDefinition<TEntity> 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!"
);
}

1
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<string, object> Items { get; }
//TODO: Switch to OnFailed (sync) and OnDisposed (sync) methods to be compatible with OnCompleted

8
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<string, object> Items { get; }
private readonly Dictionary<string, IDatabaseApi> _databaseApis;
@ -48,6 +50,7 @@ namespace Volo.Abp.Uow
_databaseApis = new Dictionary<string, IDatabaseApi>();
_transactionApis = new Dictionary<string, ITransactionApi>();
Items = new Dictionary<string, object>();
}
@ -320,10 +323,5 @@ namespace Volo.Abp.Uow
{
return $"[UnitOfWork {Id}]";
}
public Dictionary<string, object> GetHardDeleteItems()
{
return Items;
}
}
}

55
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<TStartupModule> : TestAppTestBase<TStartupModule>
where TStartupModule : IAbpModule
{
protected readonly IRepository<Person, Guid> _personRepository;
protected readonly IRepository<Person, Guid> PersonRepository;
protected readonly IDataFilter DataFilter;
protected readonly IUnitOfWorkManager _unitOfWorkManager;
public HardDelete_Tests()
protected readonly IUnitOfWorkManager UnitOfWorkManager;
protected HardDelete_Tests()
{
_personRepository = GetRequiredService<IRepository<Person, Guid>>();
PersonRepository = GetRequiredService<IRepository<Person, Guid>>();
DataFilter = GetRequiredService<IDataFilter>();
_unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
UnitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
}
[Fact]
public async Task Should_HardDelete_Entity_With_Collection()
public async Task Should_HardDelete_Entities()
{
using (var uow = _unitOfWorkManager.Begin())
{
using (DataFilter.Disable<ISoftDelete>())
{
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<ISoftDelete>())
{
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<ISoftDelete>())
{
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();
}
}
}

Loading…
Cancel
Save