Refactor and improvements for #2807

pull/2844/head
Halil İbrahim Kalkan 5 years ago
parent 8d76491209
commit fc8d34eae9

@ -112,26 +112,5 @@ namespace Volo.Abp.Domain.Entities
idProperty.SetValue(entity, idFactory()); 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);
}
} }
} }

@ -18,6 +18,7 @@ namespace Volo.Abp.Domain.Repositories
public IDataFilter DataFilter { get; set; } public IDataFilter DataFilter { get; set; }
public ICurrentTenant CurrentTenant { get; set; } public ICurrentTenant CurrentTenant { get; set; }
public IUnitOfWorkManager UnitOfWorkManager { get; set; } public IUnitOfWorkManager UnitOfWorkManager { get; set; }
public virtual Type ElementType => GetQueryable().ElementType; public virtual Type ElementType => GetQueryable().ElementType;

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; 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) public static async Task HardDeleteAsync<TEntity>(
where TEntity : class, IEntity<TPrimaryKey>, ISoftDelete 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 (!(ProxyHelper.UnProxy(repository) is IUnitOfWorkManagerAccessor unitOfWorkManagerAccessor))
if (repo != null)
{ {
var uow = ((IUnitOfWorkManagerAccessor)repo).UnitOfWorkManager; 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 baseRepository = ((RepositoryBase<TEntity>)repo); }
var items = ((IUnitOfWorkManagerAccessor)repo).UnitOfWorkManager.Current.Items;
var hardDeleteEntities = items.GetOrAdd(UnitOfWorkExtensionDataTypes.HardDelete, () => new HashSet<string>()) as HashSet<string>;
var hardDeleteKey = EntityHelper.GetHardDeleteKey(entity, baseRepository.CurrentTenant?.Id?.ToString()); var uowManager = unitOfWorkManagerAccessor.UnitOfWorkManager;
hardDeleteEntities.Add(hardDeleteKey); 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) else
where TEntity : class, IEntity<TPrimaryKey>, ISoftDelete
{
foreach (var entity in repository.Where(predicate).ToList())
{ {
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);
} }
} }
} }

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

@ -0,0 +1,7 @@
namespace Volo.Abp.Domain.Repositories
{
public static class UnitOfWorkItemNames
{
public const string HardDeletedEntities = "AbpHardDeletedEntities";
}
}

@ -208,7 +208,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
{ {
return includeDetails return includeDetails
? await WithDetails().FirstOrDefaultAsync(e => e.Id.Equals(id), GetCancellationToken(cancellationToken)) ? 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) public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default)

@ -50,6 +50,7 @@ namespace Volo.Abp.EntityFrameworkCore
public IEntityHistoryHelper EntityHistoryHelper { get; set; } public IEntityHistoryHelper EntityHistoryHelper { get; set; }
public IAuditingManager AuditingManager { get; set; } public IAuditingManager AuditingManager { get; set; }
public IUnitOfWorkManager UnitOfWorkManager { get; set; } public IUnitOfWorkManager UnitOfWorkManager { get; set; }
public IClock Clock { get; set; } public IClock Clock { get; set; }
@ -199,37 +200,24 @@ namespace Volo.Abp.EntityFrameworkCore
protected virtual void ApplyAbpConceptsForDeletedEntity(EntityEntry entry, EntityChangeReport changeReport) protected virtual void ApplyAbpConceptsForDeletedEntity(EntityEntry entry, EntityChangeReport changeReport)
{ {
if (IsHardDeleteEntity(entry)) if (TryCancelDeletionForSoftDelete(entry))
{ {
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
return;
}
CancelDeletionForSoftDelete(entry);
UpdateConcurrencyStamp(entry); UpdateConcurrencyStamp(entry);
SetDeletionAuditProperties(entry); SetDeletionAuditProperties(entry);
changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
} }
protected virtual bool IsHardDeleteEntity(EntityEntry entry) changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
{
if (UnitOfWorkManager?.Current?.Items == null)
{
return false;
} }
if (!UnitOfWorkManager.Current.Items.ContainsKey(UnitOfWorkExtensionDataTypes.HardDelete)) protected virtual bool IsHardDeleted(EntityEntry entry)
{ {
return false; var hardDeletedEntities = UnitOfWorkManager?.Current?.Items.GetOrDefault(UnitOfWorkItemNames.HardDeletedEntities) as HashSet<IEntity>;
} if (hardDeletedEntities == null)
var hardDeleteItems = UnitOfWorkManager.Current.Items[UnitOfWorkExtensionDataTypes.HardDelete];
if (!(hardDeleteItems is HashSet<string> objects))
{ {
return false; 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) protected virtual void AddDomainEvents(EntityChangeReport changeReport, object entityAsObj)
@ -283,16 +271,22 @@ namespace Volo.Abp.EntityFrameworkCore
entity.ConcurrencyStamp = Guid.NewGuid().ToString("N"); entity.ConcurrencyStamp = Guid.NewGuid().ToString("N");
} }
protected virtual void CancelDeletionForSoftDelete(EntityEntry entry) protected virtual bool TryCancelDeletionForSoftDelete(EntityEntry entry)
{ {
if (!(entry.Entity is ISoftDelete)) if (!(entry.Entity is ISoftDelete))
{ {
return; return false;
}
if (IsHardDeleted(entry))
{
return false;
} }
entry.Reload(); entry.Reload();
entry.State = EntityState.Modified; entry.State = EntityState.Modified;
entry.Entity.As<ISoftDelete>().IsDeleted = true; entry.Entity.As<ISoftDelete>().IsDeleted = true;
return true;
} }
protected virtual void CheckAndSetId(EntityEntry entry) protected virtual void CheckAndSetId(EntityEntry entry)

@ -111,7 +111,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
await ApplyAbpConceptsForDeletedEntityAsync(entity); await ApplyAbpConceptsForDeletedEntityAsync(entity);
var oldConcurrencyStamp = SetNewConcurrencyStamp(entity); var oldConcurrencyStamp = SetNewConcurrencyStamp(entity);
if (entity is ISoftDelete softDeleteEntity && !IsHardDeleteEntity(entity)) if (entity is ISoftDelete softDeleteEntity && !IsHardDeleted(entity))
{ {
softDeleteEntity.IsDeleted = true; softDeleteEntity.IsDeleted = true;
var result = await Collection.ReplaceOneAsync( var result = await Collection.ReplaceOneAsync(
@ -175,32 +175,21 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
Collection.AsQueryable() 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; return false;
} }
if (!UnitOfWorkManager.Current.Items.ContainsKey(UnitOfWorkExtensionDataTypes.HardDelete)) return hardDeletedEntities.Contains(entity);
{
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);
} }
protected virtual FilterDefinition<TEntity> CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null) protected virtual FilterDefinition<TEntity> CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null)
{ {
throw new NotImplementedException( 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!"
); );
} }

@ -9,6 +9,7 @@ namespace Volo.Abp.Uow
public interface IUnitOfWork : IDatabaseApiContainer, ITransactionApiContainer, IDisposable public interface IUnitOfWork : IDatabaseApiContainer, ITransactionApiContainer, IDisposable
{ {
Guid Id { get; } Guid Id { get; }
Dictionary<string, object> Items { get; } Dictionary<string, object> Items { get; }
//TODO: Switch to OnFailed (sync) and OnDisposed (sync) methods to be compatible with OnCompleted //TODO: Switch to OnFailed (sync) and OnDisposed (sync) methods to be compatible with OnCompleted

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
@ -31,6 +32,7 @@ namespace Volo.Abp.Uow
public IServiceProvider ServiceProvider { get; } public IServiceProvider ServiceProvider { get; }
[NotNull]
public Dictionary<string, object> Items { get; } public Dictionary<string, object> Items { get; }
private readonly Dictionary<string, IDatabaseApi> _databaseApis; private readonly Dictionary<string, IDatabaseApi> _databaseApis;
@ -48,6 +50,7 @@ namespace Volo.Abp.Uow
_databaseApis = new Dictionary<string, IDatabaseApi>(); _databaseApis = new Dictionary<string, IDatabaseApi>();
_transactionApis = new Dictionary<string, ITransactionApi>(); _transactionApis = new Dictionary<string, ITransactionApi>();
Items = new Dictionary<string, object>(); Items = new Dictionary<string, object>();
} }
@ -320,10 +323,5 @@ namespace Volo.Abp.Uow
{ {
return $"[UnitOfWork {Id}]"; return $"[UnitOfWork {Id}]";
} }
public Dictionary<string, object> GetHardDeleteItems()
{
return Items;
}
} }
} }

@ -1,7 +1,5 @@
using Shouldly; using Shouldly;
using System; using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
@ -15,58 +13,57 @@ namespace Volo.Abp.TestApp.Testing
public abstract class HardDelete_Tests<TStartupModule> : TestAppTestBase<TStartupModule> public abstract class HardDelete_Tests<TStartupModule> : TestAppTestBase<TStartupModule>
where TStartupModule : IAbpModule where TStartupModule : IAbpModule
{ {
protected readonly IRepository<Person, Guid> _personRepository; protected readonly IRepository<Person, Guid> PersonRepository;
protected readonly IDataFilter DataFilter; protected readonly IDataFilter DataFilter;
protected readonly IUnitOfWorkManager _unitOfWorkManager; protected readonly IUnitOfWorkManager UnitOfWorkManager;
public HardDelete_Tests()
protected HardDelete_Tests()
{ {
_personRepository = GetRequiredService<IRepository<Person, Guid>>(); PersonRepository = GetRequiredService<IRepository<Person, Guid>>();
DataFilter = GetRequiredService<IDataFilter>(); DataFilter = GetRequiredService<IDataFilter>();
_unitOfWorkManager = GetRequiredService<IUnitOfWorkManager>(); UnitOfWorkManager = GetRequiredService<IUnitOfWorkManager>();
} }
[Fact] [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); var douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId);
await _personRepository.HardDeleteAsync(x => x.Id == TestDataBuilder.UserDouglasId); await PersonRepository.HardDeleteAsync(douglas);
await uow.CompleteAsync();
}
var deletedDougles = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId);
deletedDougles.ShouldBeNull(); douglas.ShouldBeNull();
}
} }
[Fact] [Fact]
public async Task Should_HardDelete_Soft_Deleted_Entities() public async Task Should_HardDelete_Soft_Deleted_Entities()
{ {
var douglas = await _personRepository.GetAsync(TestDataBuilder.UserDouglasId); var douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId);
await _personRepository.DeleteAsync(douglas); await PersonRepository.DeleteAsync(douglas);
douglas = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId);
douglas.ShouldBeNull(); douglas.ShouldBeNull();
using (DataFilter.Disable<ISoftDelete>()) using (DataFilter.Disable<ISoftDelete>())
{ {
douglas = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId); douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId);
douglas.ShouldNotBeNull(); douglas.ShouldNotBeNull();
douglas.IsDeleted.ShouldBeTrue(); douglas.IsDeleted.ShouldBeTrue();
douglas.DeletionTime.ShouldNotBeNull(); douglas.DeletionTime.ShouldNotBeNull();
} }
using (var uow = _unitOfWorkManager.Begin())
using (var uow = UnitOfWorkManager.Begin())
{ {
using (DataFilter.Disable<ISoftDelete>()) using (DataFilter.Disable<ISoftDelete>())
{ {
douglas = await _personRepository.GetAsync(TestDataBuilder.UserDouglasId); douglas = await PersonRepository.GetAsync(TestDataBuilder.UserDouglasId);
await _personRepository.HardDeleteAsync(douglas);
await uow.CompleteAsync();
var deletedDougles = await _personRepository.FindAsync(TestDataBuilder.UserDouglasId);
deletedDougles.ShouldBeNull();
} }
await PersonRepository.HardDeleteAsync(douglas);
await uow.CompleteAsync();
} }
douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId);
douglas.ShouldBeNull();
} }
} }
} }

Loading…
Cancel
Save