diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs
index 3bda51bd2e..f1136c4ea2 100644
--- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs
+++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IRepository.cs
@@ -20,8 +20,10 @@ namespace Volo.Abp.Domain.Repositories
{
///
/// Get a single entity by the given .
- /// It returns null if no entity with the given .
+ ///
+ /// It returns null if there is no entity with the given .
/// It throws if there are multiple entities with the given .
+ ///
///
/// A condition to find the entity
/// Set true to include all children of this entity
@@ -34,8 +36,10 @@ namespace Volo.Abp.Domain.Repositories
///
/// Get a single entity by the given .
+ ///
/// It throws if there is no entity with the given .
/// It throws if there are multiple entities with the given .
+ ///
///
/// A condition to filter entities
/// Set true to include all children of this entity
@@ -47,10 +51,11 @@ namespace Volo.Abp.Domain.Repositories
);
///
- /// Deletes many entities by function.
- /// Notice that: All entities fits to given predicate are retrieved and deleted.
- /// This may cause major performance problems if there are too many entities with
- /// given predicate.
+ /// Deletes many entities by the given .
+ ///
+ /// Please note: This may cause major performance problems if there are too many entities returned for a
+ /// given predicate and the database provider doesn't have a way to efficiently delete many entities.
+ ///
///
/// A condition to filter entities
///
@@ -69,4 +74,4 @@ namespace Volo.Abp.Domain.Repositories
where TEntity : class, IEntity
{
}
-}
\ No newline at end of file
+}
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 055c9c2de9..d673ff2c28 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
@@ -289,10 +289,7 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore
.Where(predicate)
.ToListAsync(GetCancellationToken(cancellationToken));
- foreach (var entity in entities)
- {
- dbSet.Remove(entity);
- }
+ await DeleteManyAsync(entities, autoSave, cancellationToken);
if (autoSave)
{
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 8ab6753de3..fd8c5afd16 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
@@ -191,10 +191,7 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb
{
var entities = (await GetQueryableAsync()).Where(predicate).ToList();
- foreach (var entity in entities)
- {
- await DeleteAsync(entity, autoSave, cancellationToken);
- }
+ await DeleteManyAsync(entities, autoSave, cancellationToken);
}
public override async Task InsertAsync(
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 fc520bce81..e6030246aa 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
@@ -71,16 +71,18 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
protected Task GetDbContextAsync(CancellationToken cancellationToken = default)
{
+ cancellationToken = GetCancellationToken(cancellationToken);
+
// Multi-tenancy unaware entities should always use the host connection string
if (!EntityHelper.IsMultiTenant())
{
using (CurrentTenant.Change(null))
{
- return DbContextProvider.GetDbContextAsync(GetCancellationToken(cancellationToken));
+ return DbContextProvider.GetDbContextAsync(cancellationToken);
}
}
- return DbContextProvider.GetDbContextAsync(GetCancellationToken(cancellationToken));
+ return DbContextProvider.GetDbContextAsync(cancellationToken);
}
protected IMongoDbContextProvider DbContextProvider { get; }
@@ -107,9 +109,11 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
bool autoSave = false,
CancellationToken cancellationToken = default)
{
+ cancellationToken = GetCancellationToken(cancellationToken);
+
await ApplyAbpConceptsForAddedEntityAsync(entity);
- var dbContext = await GetDbContextAsync(GetCancellationToken(cancellationToken));
+ var dbContext = await GetDbContextAsync(cancellationToken);
var collection = dbContext.Collection();
if (dbContext.SessionHandle != null)
@@ -117,14 +121,14 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
await collection.InsertOneAsync(
dbContext.SessionHandle,
entity,
- cancellationToken: GetCancellationToken(cancellationToken)
+ cancellationToken: cancellationToken
);
}
else
{
await collection.InsertOneAsync(
entity,
- cancellationToken: GetCancellationToken(cancellationToken)
+ cancellationToken: cancellationToken
);
}
@@ -133,6 +137,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
public override async Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
+ cancellationToken = GetCancellationToken(cancellationToken);
+
var entityArray = entities.ToArray();
foreach (var entity in entityArray)
@@ -140,7 +146,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
await ApplyAbpConceptsForAddedEntityAsync(entity);
}
- var dbContext = await GetDbContextAsync(GetCancellationToken(cancellationToken));
+ var dbContext = await GetDbContextAsync(cancellationToken);
var collection = dbContext.Collection();
if (BulkOperationProvider != null)
@@ -169,6 +175,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
bool autoSave = false,
CancellationToken cancellationToken = default)
{
+ cancellationToken = GetCancellationToken(cancellationToken);
+
SetModificationAuditProperties(entity);
if (entity is ISoftDelete softDeleteEntity && softDeleteEntity.IsDeleted)
@@ -186,7 +194,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
var oldConcurrencyStamp = SetNewConcurrencyStamp(entity);
ReplaceOneResult result;
- var dbContext = await GetDbContextAsync(GetCancellationToken(cancellationToken));
+ var dbContext = await GetDbContextAsync(cancellationToken);
var collection = dbContext.Collection();
if (dbContext.SessionHandle != null)
@@ -195,7 +203,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
dbContext.SessionHandle,
CreateEntityFilter(entity, true, oldConcurrencyStamp),
entity,
- cancellationToken: GetCancellationToken(cancellationToken)
+ cancellationToken: cancellationToken
);
}
else
@@ -203,7 +211,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
result = await collection.ReplaceOneAsync(
CreateEntityFilter(entity, true, oldConcurrencyStamp),
entity,
- cancellationToken: GetCancellationToken(cancellationToken)
+ cancellationToken: cancellationToken
);
}
@@ -277,15 +285,18 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
bool autoSave = false,
CancellationToken cancellationToken = default)
{
- await ApplyAbpConceptsForDeletedEntityAsync(entity);
- var oldConcurrencyStamp = SetNewConcurrencyStamp(entity);
+ cancellationToken = GetCancellationToken(cancellationToken);
- var dbContext = await GetDbContextAsync(GetCancellationToken(cancellationToken));
+ var dbContext = await GetDbContextAsync(cancellationToken);
var collection = dbContext.Collection();
- if (entity is ISoftDelete softDeleteEntity && !IsHardDeleted(entity))
+ var oldConcurrencyStamp = SetNewConcurrencyStamp(entity);
+
+ if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && !IsHardDeleted(entity))
{
- softDeleteEntity.IsDeleted = true;
+ ((ISoftDelete)entity).IsDeleted = true;
+ await ApplyAbpConceptsForDeletedEntityAsync(entity);
+
ReplaceOneResult result;
if (dbContext.SessionHandle != null)
@@ -294,7 +305,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
dbContext.SessionHandle,
CreateEntityFilter(entity, true, oldConcurrencyStamp),
entity,
- cancellationToken: GetCancellationToken(cancellationToken)
+ cancellationToken: cancellationToken
);
}
else
@@ -302,7 +313,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
result = await collection.ReplaceOneAsync(
CreateEntityFilter(entity, true, oldConcurrencyStamp),
entity,
- cancellationToken: GetCancellationToken(cancellationToken)
+ cancellationToken: cancellationToken
);
}
@@ -313,6 +324,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
}
else
{
+ await ApplyAbpConceptsForDeletedEntityAsync(entity);
+
DeleteResult result;
if (dbContext.SessionHandle != null)
@@ -320,14 +333,14 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
result = await collection.DeleteOneAsync(
dbContext.SessionHandle,
CreateEntityFilter(entity, true, oldConcurrencyStamp),
- cancellationToken: GetCancellationToken(cancellationToken)
+ cancellationToken: cancellationToken
);
}
else
{
result = await collection.DeleteOneAsync(
CreateEntityFilter(entity, true, oldConcurrencyStamp),
- GetCancellationToken(cancellationToken)
+ cancellationToken
);
}
@@ -343,55 +356,55 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
bool autoSave = false,
CancellationToken cancellationToken = default)
{
- var softDeletedEntities = new List();
+ cancellationToken = GetCancellationToken(cancellationToken);
+
+ var softDeletedEntities = new Dictionary();
var hardDeletedEntities = new List();
foreach (var entity in entities)
{
- await ApplyAbpConceptsForDeletedEntityAsync(entity);
- SetNewConcurrencyStamp(entity);
-
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && !IsHardDeleted(entity))
{
- softDeletedEntities.Add(entity);
+ ((ISoftDelete)entity).IsDeleted = true;
+
+ softDeletedEntities.Add(entity, SetNewConcurrencyStamp(entity));
}
else
{
hardDeletedEntities.Add(entity);
}
+
+ await ApplyAbpConceptsForDeletedEntityAsync(entity);
}
- var dbContext = await GetDbContextAsync(GetCancellationToken(cancellationToken));
+ var dbContext = await GetDbContextAsync(cancellationToken);
var collection = dbContext.Collection();
if (BulkOperationProvider != null)
{
- await BulkOperationProvider.DeleteManyAsync(this, entities.ToArray(), dbContext.SessionHandle, autoSave, cancellationToken);
+ await BulkOperationProvider.DeleteManyAsync(this, entities, dbContext.SessionHandle, autoSave, cancellationToken);
return;
}
if (softDeletedEntities.Count > 0)
{
- UpdateResult updateResult;
- var softDeleteEntitiesCount = softDeletedEntities.Count;
+ BulkWriteResult updateResult;
+
+ var replaceRequests = new List>(
+ softDeletedEntities.Select(entity => new ReplaceOneModel(
+ CreateEntityFilter(entity.Key, true, entity.Value), entity.Key))
+ );
if (dbContext.SessionHandle != null)
{
- updateResult = await collection.UpdateManyAsync(
- dbContext.SessionHandle,
- CreateEntitiesFilter(softDeletedEntities),
- Builders.Update.Set(x => ((ISoftDelete)x).IsDeleted, true)
- );
+ updateResult = await collection.BulkWriteAsync(dbContext.SessionHandle, replaceRequests, cancellationToken: cancellationToken);
}
else
{
- updateResult = await collection.UpdateManyAsync(
- CreateEntitiesFilter(softDeletedEntities),
- Builders.Update.Set(x => ((ISoftDelete)x).IsDeleted, true)
- );
+ updateResult = await collection.BulkWriteAsync(replaceRequests, cancellationToken: cancellationToken);
}
- if (updateResult.MatchedCount < softDeleteEntitiesCount)
+ if (updateResult.MatchedCount < softDeletedEntities.Count)
{
ThrowOptimisticConcurrencyException();
}
@@ -406,14 +419,14 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
{
deleteResult = await collection.DeleteManyAsync(
dbContext.SessionHandle,
- CreateEntitiesFilter(hardDeletedEntities)
- );
+ CreateEntitiesFilter(hardDeletedEntities),
+ cancellationToken: cancellationToken);
}
else
{
deleteResult = await collection.DeleteManyAsync(
- CreateEntitiesFilter(hardDeletedEntities)
- );
+ CreateEntitiesFilter(hardDeletedEntities),
+ cancellationToken: cancellationToken);
}
if (deleteResult.DeletedCount < hardDeletedEntitiesCount)
@@ -462,10 +475,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
.Where(predicate)
.ToListAsync(cancellationToken);
- foreach (var entity in entities)
- {
- await DeleteAsync(entity, autoSave, cancellationToken);
- }
+ await DeleteManyAsync(entities, autoSave, cancellationToken);
}
[Obsolete("Use GetQueryableAsync method.")]
@@ -484,9 +494,11 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
+ cancellationToken = GetCancellationToken(cancellationToken);
+
return await (await GetMongoQueryableAsync(cancellationToken))
.Where(predicate)
- .SingleOrDefaultAsync(GetCancellationToken(cancellationToken));
+ .SingleOrDefaultAsync(cancellationToken);
}
[Obsolete("Use GetMongoQueryableAsync method.")]
@@ -501,6 +513,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
public async Task> GetMongoQueryableAsync(CancellationToken cancellationToken = default)
{
+ cancellationToken = GetCancellationToken(cancellationToken);
+
var dbContext = await GetDbContextAsync(cancellationToken);
var collection = dbContext.Collection();
@@ -513,6 +527,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
public async Task> GetAggregateAsync(CancellationToken cancellationToken = default)
{
+ cancellationToken = GetCancellationToken(cancellationToken);
+
var dbContext = await GetDbContextAsync(cancellationToken);
var collection = await GetCollectionAsync(cancellationToken);
@@ -695,13 +711,13 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
[Obsolete("This method will be removed in future versions.")]
public IAsyncCursor ToCursor(CancellationToken cancellationToken = new CancellationToken())
{
- return GetMongoQueryable().ToCursor(cancellationToken);
+ return GetMongoQueryable().ToCursor(GetCancellationToken(cancellationToken));
}
[Obsolete("This method will be removed in future versions.")]
public Task> ToCursorAsync(CancellationToken cancellationToken = new CancellationToken())
{
- return GetMongoQueryable().ToCursorAsync(cancellationToken);
+ return GetMongoQueryable().ToCursorAsync(GetCancellationToken(cancellationToken));
}
}
@@ -724,7 +740,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB
bool includeDetails = true,
CancellationToken cancellationToken = default)
{
- var entity = await FindAsync(id, includeDetails, cancellationToken);
+ var entity = await FindAsync(id, includeDetails, GetCancellationToken(cancellationToken));
if (entity == null)
{
diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Tests.cs
index b8618fd6a3..4b313d2127 100644
--- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Tests.cs
+++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Tests.cs
@@ -56,6 +56,23 @@ namespace Volo.Abp.TestApp.Testing
douglas.DeletionTime.ShouldNotBeNull();
}
}
+
+ [Fact]
+ public async Task Should_Cancel_Deletion_For_Soft_Delete_Many_Entities_ById()
+ {
+ await PersonRepository.DeleteManyAsync(new []{ TestDataBuilder.UserDouglasId });
+
+ var douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId);
+ douglas.ShouldBeNull();
+
+ using (DataFilter.Disable())
+ {
+ douglas = await PersonRepository.FindAsync(TestDataBuilder.UserDouglasId);
+ douglas.ShouldNotBeNull();
+ douglas.IsDeleted.ShouldBeTrue();
+ douglas.DeletionTime.ShouldNotBeNull();
+ }
+ }
[Fact]
public async Task Should_Handle_Deletion_On_Update_For_Soft_Delete_Entities()