From 6a3e229837dcde31b90d0e1e06309564462f7871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Tue, 15 Dec 2020 16:46:28 +0300 Subject: [PATCH 01/19] Draft implementation of IRepository.InsertManyAsync(); --- common.DotSettings | 1 + .../Repositories/BasicRepositoryBase.cs | 40 +++++++++++++++++-- .../Domain/Repositories/IBasicRepository.cs | 7 +++- .../Abp/Domain/Repositories/RepositoryBase.cs | 8 ---- .../EntityFrameworkCore/EfCoreRepository.cs | 23 +++++++++++ .../IEfCoreBulkOperationProvider.cs | 20 ++++++++++ 6 files changed, 86 insertions(+), 13 deletions(-) create mode 100644 framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreBulkOperationProvider.cs diff --git a/common.DotSettings b/common.DotSettings index 7edbd05e75..5c1cda48fd 100644 --- a/common.DotSettings +++ b/common.DotSettings @@ -29,6 +29,7 @@ Never Never Never + True True True False diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs index ae1b03475c..1235dc3714 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs @@ -2,15 +2,18 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities; +using Volo.Abp.Linq; +using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; using Volo.Abp.Uow; namespace Volo.Abp.Domain.Repositories { - public abstract class BasicRepositoryBase : - IBasicRepository, + public abstract class BasicRepositoryBase : + IBasicRepository, IServiceProviderAccessor, IUnitOfWorkEnabled, ITransientDependency @@ -18,6 +21,14 @@ namespace Volo.Abp.Domain.Repositories { public IServiceProvider ServiceProvider { get; set; } + public IDataFilter DataFilter { get; set; } + + public ICurrentTenant CurrentTenant { get; set; } + + public IAsyncQueryableExecuter AsyncExecuter { get; set; } + + public IUnitOfWorkManager UnitOfWorkManager { get; set; } + public ICancellationTokenProvider CancellationTokenProvider { get; set; } protected BasicRepositoryBase() @@ -27,6 +38,29 @@ namespace Volo.Abp.Domain.Repositories public abstract Task InsertAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); + public virtual async Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + foreach (var entity in entities) + { + await InsertAsync(entity, cancellationToken: cancellationToken); + } + + if (autoSave) + { + await SaveChangesAsync(cancellationToken); + } + } + + protected virtual Task SaveChangesAsync(CancellationToken cancellationToken) + { + if (UnitOfWorkManager?.Current != null) + { + return UnitOfWorkManager?.Current.SaveChangesAsync(cancellationToken); + } + + return Task.CompletedTask; + } + public abstract Task UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); public abstract Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); @@ -59,7 +93,7 @@ namespace Volo.Abp.Domain.Repositories } public abstract Task FindAsync(TKey id, bool includeDetails = true, CancellationToken cancellationToken = default); - + public virtual async Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default) { var entity = await FindAsync(id, cancellationToken: cancellationToken); diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs index 6b62691ed8..0637225229 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs @@ -1,4 +1,5 @@ -using System.Threading; +using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Volo.Abp.Domain.Entities; @@ -20,8 +21,10 @@ namespace Volo.Abp.Domain.Repositories [NotNull] Task InsertAsync([NotNull] TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); + Task InsertManyAsync([NotNull] IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default); + /// - /// Updates an existing entity. + /// Updates an existing entity. /// /// /// Set true to automatically save changes to database. 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 fd66b293c9..6c4d7a31f4 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 @@ -16,14 +16,6 @@ namespace Volo.Abp.Domain.Repositories public abstract class RepositoryBase : BasicRepositoryBase, IRepository, IUnitOfWorkManagerAccessor where TEntity : class, IEntity { - public IDataFilter DataFilter { get; set; } - - public ICurrentTenant CurrentTenant { get; set; } - - public IAsyncQueryableExecuter AsyncExecuter { get; set; } - - public IUnitOfWorkManager UnitOfWorkManager { get; set; } - public virtual Type ElementType => GetQueryable().ElementType; public virtual Expression Expression => GetQueryable().Expression; 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 0d44fd1249..c8e4b6e7e4 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 @@ -32,6 +32,8 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore public virtual IGuidGenerator GuidGenerator { get; set; } + public IEfCoreBulkOperationProvider BulkOperationProvider { get; set; } + public EfCoreRepository(IDbContextProvider dbContextProvider) { _dbContextProvider = dbContextProvider; @@ -110,11 +112,32 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore .ToListAsync(GetCancellationToken(cancellationToken)); } + public override async Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + if (BulkOperationProvider != null) + { + await BulkOperationProvider.InsertManyAsync( + this, + entities, + autoSave, + cancellationToken + ); + return; + } + + await base.InsertManyAsync(entities, autoSave, cancellationToken); + } + protected override IQueryable GetQueryable() { return DbSet.AsQueryable(); } + protected override Task SaveChangesAsync(CancellationToken cancellationToken) + { + return DbContext.SaveChangesAsync(cancellationToken); + } + public async override Task FindAsync( Expression> predicate, bool includeDetails = true, diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreBulkOperationProvider.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreBulkOperationProvider.cs new file mode 100644 index 0000000000..6ff6e2bdc0 --- /dev/null +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreBulkOperationProvider.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities; +using Volo.Abp.EntityFrameworkCore; + +namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore +{ + public interface IEfCoreBulkOperationProvider + { + Task InsertManyAsync( + IEfCoreRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken + ) + where TDbContext : IEfCoreDbContext + where TEntity : class, IEntity; + } +} From 4e4b2454c02298ecb9ff95d3401046971d1725e1 Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 15 Dec 2020 17:48:56 +0300 Subject: [PATCH 02/19] Add Bulk Operation method to IBasicRepository - Related with #6654 --- .../Domain/Repositories/IBasicRepository.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs index 0637225229..24ec027b06 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs @@ -21,6 +21,16 @@ namespace Volo.Abp.Domain.Repositories [NotNull] Task InsertAsync([NotNull] TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); + /// + /// Inserts multiple new entities. + /// + /// + /// Set true to automatically save changes to database. + /// This is useful for ORMs / database APIs those only save changes with an explicit method call, but you need to immediately save changes to the database. + /// + /// A to observe while waiting for the task to complete. + /// Entities to be inserted. + /// Awaitable . Task InsertManyAsync([NotNull] IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default); /// @@ -35,6 +45,17 @@ namespace Volo.Abp.Domain.Repositories [NotNull] Task UpdateAsync([NotNull] TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); + /// + /// Updates multiple entities. + /// + /// Entities to be updated. + /// + /// Set true to automatically save changes to database. + /// This is useful for ORMs / database APIs those only save changes with an explicit method call, but you need to immediately save changes to the database. + /// A to observe while waiting for the task to complete. + /// Awaitable . + + Task UpdateManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default); /// /// Deletes an entity. /// @@ -45,6 +66,18 @@ namespace Volo.Abp.Domain.Repositories /// /// A to observe while waiting for the task to complete. Task DeleteAsync([NotNull] TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); + + /// + /// Deletes multiple entities. + /// + /// Entities to be deleted. + /// + /// Set true to automatically save changes to database. + /// This is useful for ORMs / database APIs those only save changes with an explicit method call, but you need to immediately save changes to the database. + /// + /// A to observe while waiting for the task to complete. + /// Awaitable . + Task DeleteManyAsync([NotNull] IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default); } public interface IBasicRepository : IBasicRepository, IReadOnlyBasicRepository From dd01731d12b9cd010abee2184aed0144484001ea Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 15 Dec 2020 17:49:29 +0300 Subject: [PATCH 03/19] Default implementation of Bulk Operations - Related with #6654 --- .../Repositories/BasicRepositoryBase.cs | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs index 1235dc3714..4be7954a35 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs @@ -55,7 +55,7 @@ namespace Volo.Abp.Domain.Repositories { if (UnitOfWorkManager?.Current != null) { - return UnitOfWorkManager?.Current.SaveChangesAsync(cancellationToken); + return UnitOfWorkManager.Current.SaveChangesAsync(cancellationToken); } return Task.CompletedTask; @@ -63,8 +63,34 @@ namespace Volo.Abp.Domain.Repositories public abstract Task UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); + public virtual async Task UpdateManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + foreach (var entity in entities) + { + await UpdateAsync(entity, cancellationToken: cancellationToken); + } + + if (autoSave) + { + await SaveChangesAsync(cancellationToken); + } + } + public abstract Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default); + public virtual async Task DeleteManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + foreach (var entity in entities) + { + await DeleteAsync(entity, cancellationToken: cancellationToken); + } + + if (autoSave) + { + await SaveChangesAsync(cancellationToken); + } + } + public abstract Task> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default); public abstract Task GetCountAsync(CancellationToken cancellationToken = default); From bc8ea33ec7dd6538012dc017a06fcfc2c32ffcb9 Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 15 Dec 2020 18:10:56 +0300 Subject: [PATCH 04/19] Add BulkOperationProviders for both Db Provider - Related with #6654 --- .../IEfCoreBulkOperationProvider.cs | 20 ++++++++++ .../MongoDB/IMongoDbBulkOperationProvider.cs | 37 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreBulkOperationProvider.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreBulkOperationProvider.cs index 6ff6e2bdc0..55883cb9ad 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreBulkOperationProvider.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/Domain/Repositories/EntityFrameworkCore/IEfCoreBulkOperationProvider.cs @@ -16,5 +16,25 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore ) where TDbContext : IEfCoreDbContext where TEntity : class, IEntity; + + + Task UpdateManyAsync( + IEfCoreRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken + ) + where TDbContext : IEfCoreDbContext + where TEntity : class, IEntity; + + + Task DeleteManyAsync( + IEfCoreRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken + ) + where TDbContext : IEfCoreDbContext + where TEntity : class, IEntity; } } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs new file mode 100644 index 0000000000..a4751839da --- /dev/null +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories.MongoDB; + +namespace Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB +{ + public interface IMongoDbBulkOperationProvider + { + Task InsertManyAsync( + IMongoDbRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken + ) + where TEntity : class, IEntity; + + + Task UpdateManyAsync( + IMongoDbRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken + ) + where TEntity : class, IEntity; + + + Task DeleteManyAsync( + IMongoDbRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken + ) + where TEntity : class, IEntity; + } +} From dc3e4a7a44e15b86759eddff7a4b0400e1d841fa Mon Sep 17 00:00:00 2001 From: enisn Date: Tue, 15 Dec 2020 18:27:30 +0300 Subject: [PATCH 05/19] Draft provider specified implementation of Bulk Operations --- .../Repositories/BasicRepositoryBase.cs | 16 ++++- .../Domain/Repositories/IBasicRepository.cs | 14 ++++- .../Abp/Domain/Repositories/RepositoryBase.cs | 18 +++++- .../EntityFrameworkCore/EfCoreRepository.cs | 60 ++++++++++++++++--- .../MemoryDb/MemoryDbRepository.cs | 14 +++++ .../Repositories/MongoDB/MongoDbRepository.cs | 6 ++ 6 files changed, 116 insertions(+), 12 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs index 4be7954a35..f9100d43bb 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/BasicRepositoryBase.cs @@ -1,4 +1,5 @@ -using System; +using JetBrains.Annotations; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -130,5 +131,18 @@ namespace Volo.Abp.Domain.Repositories await DeleteAsync(entity, autoSave, cancellationToken); } + + public async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) + { + foreach (var id in ids) + { + await DeleteAsync(id, cancellationToken: cancellationToken); + } + + if (autoSave) + { + await SaveChangesAsync(cancellationToken); + } + } } } diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs index 24ec027b06..06a222cda7 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs @@ -55,7 +55,7 @@ namespace Volo.Abp.Domain.Repositories /// A to observe while waiting for the task to complete. /// Awaitable . - Task UpdateManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default); + Task UpdateManyAsync([NotNull] IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default); /// /// Deletes an entity. /// @@ -93,5 +93,17 @@ namespace Volo.Abp.Domain.Repositories /// /// A to observe while waiting for the task to complete. Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default); //TODO: Return true if deleted + + /// + /// Deletes multiple entities by primary keys. + /// + /// Primary keys of the each entity. + /// + /// Set true to automatically save changes to database. + /// This is useful for ORMs / database APIs those only save changes with an explicit method call, but you need to immediately save changes to the database. + /// + /// A to observe while waiting for the task to complete. + /// Awaitable . + Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default); } } 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 6c4d7a31f4..db0ec2b11c 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 @@ -1,4 +1,5 @@ -using System; +using JetBrains.Annotations; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -101,5 +102,20 @@ namespace Volo.Abp.Domain.Repositories await DeleteAsync(entity, autoSave, cancellationToken); } + + + + public async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) + { + foreach (var id in ids) + { + await DeleteAsync(id, cancellationToken: cancellationToken); + } + + if (autoSave) + { + await SaveChangesAsync(cancellationToken); + } + } } } 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 c8e4b6e7e4..4866b9daea 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 @@ -1,13 +1,14 @@ -using System; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System; using System.Collections.Generic; using System.Linq; using System.Linq.Dynamic.Core; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using Volo.Abp.Domain.Entities; using Volo.Abp.EntityFrameworkCore; using Volo.Abp.EntityFrameworkCore.DependencyInjection; @@ -112,20 +113,48 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore .ToListAsync(GetCancellationToken(cancellationToken)); } - public override async Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + public override Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) { if (BulkOperationProvider != null) { - await BulkOperationProvider.InsertManyAsync( + return BulkOperationProvider.InsertManyAsync( this, entities, autoSave, cancellationToken ); - return; } - await base.InsertManyAsync(entities, autoSave, cancellationToken); + return base.InsertManyAsync(entities, autoSave, cancellationToken); + } + + public override Task UpdateManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + if (BulkOperationProvider != null) + { + return BulkOperationProvider.UpdateManyAsync( + this, + entities, + autoSave, + cancellationToken + ); + } + + return base.UpdateManyAsync(entities, autoSave, cancellationToken); + } + + public override Task DeleteManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + if (BulkOperationProvider != null) + { + return BulkOperationProvider.DeleteManyAsync( + this, + entities, + autoSave, + cancellationToken); + } + + return base.DeleteManyAsync(entities, autoSave, cancellationToken); } protected override IQueryable GetQueryable() @@ -275,7 +304,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) @@ -288,5 +317,18 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore await DeleteAsync(entity, autoSave, cancellationToken); } + + public async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) + { + foreach (var id in ids) + { + await DeleteAsync(id, cancellationToken: cancellationToken); + } + + if (autoSave) + { + await SaveChangesAsync(cancellationToken); + } + } } } 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 bb77149a29..63203d8b0f 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 @@ -1,3 +1,4 @@ +using JetBrains.Annotations; using System; using System.Collections.Generic; using System.Linq; @@ -309,5 +310,18 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb { await DeleteAsync(x => x.Id.Equals(id), autoSave, cancellationToken); } + + public virtual async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) + { + foreach (var id in ids) + { + await DeleteAsync(id, cancellationToken: cancellationToken); + } + + if (autoSave) + { + await SaveChangesAsync(cancellationToken); + } + } } } 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 6af98da56b..35d75629b0 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 @@ -1,3 +1,4 @@ +using JetBrains.Annotations; using MongoDB.Driver; using MongoDB.Driver.Linq; using System; @@ -481,5 +482,10 @@ namespace Volo.Abp.Domain.Repositories.MongoDB { return RepositoryFilterer.CreateEntityFilter(entity, withConcurrencyStamp, concurrencyStamp); } + + public async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } } From 0a96808d15bc3e33693fa73da853bf06e9094474 Mon Sep 17 00:00:00 2001 From: enisn Date: Wed, 16 Dec 2020 11:50:22 +0300 Subject: [PATCH 06/19] Add multiple entity filtering to MongoDbRepositoryFilterer --- .../MongoDB/IMongoDbRepositoryFilterer.cs | 21 +++++++++++++++ .../MongoDB/MongoDbRepositoryFilterer.cs | 27 ++++++++++++++++--- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs index 1790c8af6b..5a96a29022 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs @@ -14,5 +14,26 @@ namespace Volo.Abp.Domain.Repositories.MongoDB FilterDefinition CreateEntityFilter(TKey id, bool applyFilters = false); FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null); + + /// + /// Creates 'In' filter for mongoDb. + /// + /// + /// Visit https://docs.mongodb.com/manual/reference/operator/query/in/ to get more information about 'in' operator. + /// + /// Entities to be filtered. + /// Set true to use GlobalFilters. Default is false. + /// Created . + FilterDefinition CreateEntitiesFilter(IEnumerable entities, bool applyFilters = false); + + /// + /// Creates 'In' filter for mongoDb. + /// + /// + /// Visit https://docs.mongodb.com/manual/reference/operator/query/in/ to get more information about 'in' operator. + /// + /// Entity Ids to be filtered. + /// Set true to use GlobalFilters. Default is false. + FilterDefinition CreateEntitiesFilter(IEnumerable ids, bool applyFilters = false); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepositoryFilterer.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepositoryFilterer.cs index 4c07c747f3..bee66efba5 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepositoryFilterer.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepositoryFilterer.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using MongoDB.Driver; using Volo.Abp.Data; using Volo.Abp.Domain.Entities; @@ -23,13 +24,13 @@ namespace Volo.Abp.Domain.Repositories.MongoDB { if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && DataFilter.IsEnabled()) { - filters.Add(Builders.Filter.Eq(e => ((ISoftDelete) e).IsDeleted, false)); + filters.Add(Builders.Filter.Eq(e => ((ISoftDelete)e).IsDeleted, false)); } if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) { var tenantId = CurrentTenant.Id; - filters.Add(Builders.Filter.Eq(e => ((IMultiTenant) e).TenantId, tenantId)); + filters.Add(Builders.Filter.Eq(e => ((IMultiTenant)e).TenantId, tenantId)); } } } @@ -72,8 +73,28 @@ namespace Volo.Abp.Domain.Repositories.MongoDB return Builders.Filter.And( Builders.Filter.Eq(e => e.Id, entity.Id), - Builders.Filter.Eq(e => ((IHasConcurrencyStamp) e).ConcurrencyStamp, concurrencyStamp) + Builders.Filter.Eq(e => ((IHasConcurrencyStamp)e).ConcurrencyStamp, concurrencyStamp) ); } + + public FilterDefinition CreateEntitiesFilter(IEnumerable entities, bool applyFilters = false) + { + return CreateEntitiesFilter(entities.Select(s => s.Id), applyFilters); + } + + public FilterDefinition CreateEntitiesFilter(IEnumerable ids, bool applyFilters = false) + { + var filters = new List>() + { + Builders.Filter.In(e => e.Id, ids), + }; + + if (applyFilters) + { + AddGlobalFilters(filters); + } + + return Builders.Filter.And(filters); + } } } \ No newline at end of file From 7d39d8a37fc563e6509646128f1b07baac8ee3fa Mon Sep 17 00:00:00 2001 From: enisn Date: Wed, 16 Dec 2020 11:50:56 +0300 Subject: [PATCH 07/19] Implementation of Bulk Operations to MonboDbRepository - InsertMany - UpdateMany - DeleteMany - Related with #6654 --- .../Repositories/MongoDB/MongoDbRepository.cs | 146 +++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) 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 35d75629b0..308b7d98ed 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 @@ -82,6 +82,28 @@ namespace Volo.Abp.Domain.Repositories.MongoDB return entity; } + public override async Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + foreach (var entity in entities) + { + await ApplyAbpConceptsForAddedEntityAsync(entity); + } + + if (SessionHandle != null) + { + await Collection.InsertManyAsync( + SessionHandle, + entities, + cancellationToken: cancellationToken); + } + else + { + await Collection.InsertManyAsync( + entities, + cancellationToken: cancellationToken); + } + } + public async override Task UpdateAsync( TEntity entity, bool autoSave = false, @@ -132,6 +154,55 @@ namespace Volo.Abp.Domain.Repositories.MongoDB return entity; } + public override async Task UpdateManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + var isSoftDeleteEntity = typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)); + + foreach (var entity in entities) + { + SetModificationAuditProperties(entity); + + if (isSoftDeleteEntity) + { + SetDeletionAuditProperties(entity); + await TriggerEntityDeleteEventsAsync(entity); + } + else + { + await TriggerEntityUpdateEventsAsync(entity); + } + + await TriggerDomainEventsAsync(entity); + + SetNewConcurrencyStamp(entity); + } + + var entitiesCount = entities.Count(); + BulkWriteResult result; + + if (SessionHandle != null) + { + result = await Collection.BulkWriteAsync(SessionHandle, GetReplaceRequests()); + } + else + { + result = await Collection.BulkWriteAsync(GetReplaceRequests()); + } + + if (result.MatchedCount < entitiesCount) + { + ThrowOptimisticConcurrencyException(); + } + + IEnumerable> GetReplaceRequests() + { + foreach (var entity in entities) + { + yield return new ReplaceOneModel(CreateEntityFilter(entity), entity); + } + } + } + public async override Task DeleteAsync( TEntity entity, bool autoSave = false, @@ -195,6 +266,67 @@ namespace Volo.Abp.Domain.Repositories.MongoDB } } + public override async Task DeleteManyAsync( + IEnumerable entities, + bool autoSave = false, + CancellationToken cancellationToken = default) + { + foreach (var entity in entities) + { + await ApplyAbpConceptsForDeletedEntityAsync(entity); + var oldConcurrencyStamp = SetNewConcurrencyStamp(entity); + } + + var entitiesCount = entities.Count(); + + if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) + { + UpdateResult updateResult; + if (SessionHandle != null) + { + updateResult = await Collection.UpdateManyAsync( + SessionHandle, + CreateEntitiesFilter(entities), + Builders.Update.Set(x => ((ISoftDelete)x).IsDeleted, true) + ); + } + else + { + updateResult = await Collection.UpdateManyAsync( + CreateEntitiesFilter(entities), + Builders.Update.Set(x => ((ISoftDelete)x).IsDeleted, true) + ); + } + + if (updateResult.MatchedCount < entitiesCount) + { + ThrowOptimisticConcurrencyException(); + } + } + else + { + DeleteResult deleteResult; + if (SessionHandle != null) + { + deleteResult = await Collection.DeleteManyAsync( + SessionHandle, + CreateEntitiesFilter(entities) + ); + } + else + { + deleteResult = await Collection.DeleteManyAsync( + CreateEntitiesFilter(entities) + ); + } + + if (deleteResult.DeletedCount < entitiesCount) + { + ThrowOptimisticConcurrencyException(); + } + } + } + public async override Task> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default) { return await GetMongoQueryable().ToListAsync(GetCancellationToken(cancellationToken)); @@ -271,6 +403,13 @@ namespace Volo.Abp.Domain.Repositories.MongoDB ); } + protected virtual FilterDefinition CreateEntitiesFilter(IEnumerable entities, bool withConcurrencyStamp = false) + { + throw new NotImplementedException( + $"{nameof(CreateEntitiesFilter)} is not implemented for MongoDB by default. It should be overriden and implemented by the deriving class!" + ); + } + protected virtual async Task ApplyAbpConceptsForAddedEntityAsync(TEntity entity) { CheckAndSetId(entity); @@ -483,9 +622,14 @@ namespace Volo.Abp.Domain.Repositories.MongoDB return RepositoryFilterer.CreateEntityFilter(entity, withConcurrencyStamp, concurrencyStamp); } + protected override FilterDefinition CreateEntitiesFilter(IEnumerable entities, bool withConcurrencyStamp = false) + { + return RepositoryFilterer.CreateEntitiesFilter(entities, withConcurrencyStamp); + } + public async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + } } } From a1a0f99bdbd14ec17e9075bde2c9955ffecbff2d Mon Sep 17 00:00:00 2001 From: enisn Date: Wed, 16 Dec 2020 12:33:48 +0300 Subject: [PATCH 08/19] Implementation of IMongoDbBulkOperationProvider into repository --- .../MongoDB/IMongoDbBulkOperationProvider.cs | 4 ++-- .../Repositories/MongoDB/MongoDbRepository.cs | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs index a4751839da..367a70bb86 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs @@ -17,7 +17,7 @@ namespace Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB where TEntity : class, IEntity; - Task UpdateManyAsync( + Task UpdateManyAsync( IMongoDbRepository repository, IEnumerable entities, bool autoSave, @@ -26,7 +26,7 @@ namespace Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB where TEntity : class, IEntity; - Task DeleteManyAsync( + Task DeleteManyAsync( IMongoDbRepository repository, IEnumerable entities, bool autoSave, 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 308b7d98ed..938899d2c2 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 @@ -16,6 +16,7 @@ using Volo.Abp.EventBus.Distributed; using Volo.Abp.EventBus.Local; using Volo.Abp.Guids; using Volo.Abp.MongoDB; +using Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB; namespace Volo.Abp.Domain.Repositories.MongoDB { @@ -46,6 +47,8 @@ namespace Volo.Abp.Domain.Repositories.MongoDB public IAuditPropertySetter AuditPropertySetter { get; set; } + public IMongoDbBulkOperationProvider BulkOperationProvider { get; set; } + public MongoDbRepository(IMongoDbContextProvider dbContextProvider) { DbContextProvider = dbContextProvider; @@ -89,6 +92,12 @@ namespace Volo.Abp.Domain.Repositories.MongoDB await ApplyAbpConceptsForAddedEntityAsync(entity); } + if (BulkOperationProvider != null) + { + await BulkOperationProvider.InsertManyAsync(this, entities, autoSave, cancellationToken); + return; + } + if (SessionHandle != null) { await Collection.InsertManyAsync( @@ -177,6 +186,12 @@ namespace Volo.Abp.Domain.Repositories.MongoDB SetNewConcurrencyStamp(entity); } + if (BulkOperationProvider != null) + { + await BulkOperationProvider.UpdateManyAsync(this, entities, autoSave, cancellationToken); + return; + } + var entitiesCount = entities.Count(); BulkWriteResult result; @@ -277,6 +292,12 @@ namespace Volo.Abp.Domain.Repositories.MongoDB var oldConcurrencyStamp = SetNewConcurrencyStamp(entity); } + if (BulkOperationProvider != null) + { + await BulkOperationProvider.DeleteManyAsync(this, entities, autoSave, cancellationToken); + return; + } + var entitiesCount = entities.Count(); if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) From 3f51368ac398597d076e9c9f43fa6086dde550b2 Mon Sep 17 00:00:00 2001 From: enisn Date: Wed, 16 Dec 2020 12:48:34 +0300 Subject: [PATCH 09/19] Blank implementation for RepositoryRegistration_Tests --- .../Abp/Domain/Repositories/RepositoryRegistration_Tests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs index 57b8054784..baf49f4314 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Repositories/RepositoryRegistration_Tests.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; +using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Entities; @@ -305,6 +306,11 @@ namespace Volo.Abp.Domain.Repositories { throw new NotImplementedException(); } + + public Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } } public class MyTestCustomBaseRepository : MyTestDefaultRepository From 855f1eec226dd7724d28be5abcb5b6dca73bcac0 Mon Sep 17 00:00:00 2001 From: enisn Date: Wed, 16 Dec 2020 15:59:31 +0300 Subject: [PATCH 10/19] DeleteMany with id implementation for MongoRepository --- .../Repositories/MongoDB/MongoDbRepository.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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 938899d2c2..7c910d350f 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 @@ -638,6 +638,15 @@ namespace Volo.Abp.Domain.Repositories.MongoDB return DeleteAsync(x => x.Id.Equals(id), autoSave, cancellationToken); } + public virtual async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) + { + var entities = await GetMongoQueryable() + .Where(x => ids.Contains(x.Id)) + .ToListAsync(GetCancellationToken(cancellationToken)); + + await DeleteManyAsync(entities, autoSave, cancellationToken); + } + protected override FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null) { return RepositoryFilterer.CreateEntityFilter(entity, withConcurrencyStamp, concurrencyStamp); @@ -647,10 +656,5 @@ namespace Volo.Abp.Domain.Repositories.MongoDB { return RepositoryFilterer.CreateEntitiesFilter(entities, withConcurrencyStamp); } - - public async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) - { - - } } } From 87e9d02248a74f86b5b689ec95e48d2f7379999c Mon Sep 17 00:00:00 2001 From: enisn Date: Wed, 16 Dec 2020 15:59:48 +0300 Subject: [PATCH 11/19] Bulk Operations Simple tests --- .../TestApp/Testing/Repository_Basic_Tests.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs index a93123c1ca..a1b08194c3 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/Repository_Basic_Tests.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Shouldly; using Volo.Abp.Domain.Repositories; @@ -115,5 +117,72 @@ namespace Volo.Abp.TestApp.Testing person.Id.ShouldNotBe(Guid.Empty); } + + [Fact] + public async Task InserManyAsync() + { + var entities = new List + { + new Person(Guid.NewGuid(), "Person 1", 30), + new Person(Guid.NewGuid(), "Person 2", 31), + new Person(Guid.NewGuid(), "Person 3", 32), + new Person(Guid.NewGuid(), "Person 4", 33), + }; + + await PersonRepository.InsertManyAsync(entities); + + foreach (var entity in entities) + { + var person = await PersonRepository.FindAsync(entity.Id); + person.ShouldNotBeNull(); + } + } + + [Fact] + public async Task UpdateManyAsync() + { + var entities = await PersonRepository.GetListAsync(); + var random = new Random(); + entities.ForEach(f => f.Age = random.Next()); + + await PersonRepository.UpdateManyAsync(entities); + + foreach (var entity in entities) + { + var person = await PersonRepository.FindAsync(entity.Id); + person.ShouldNotBeNull(); + person.Age.ShouldBe(entity.Age); + } + } + + [Fact] + public async Task DeleteManyAsync() + { + var entities = await PersonRepository.GetListAsync(); + + await PersonRepository.DeleteManyAsync(entities); + + foreach (var entity in entities) + { + var person = await PersonRepository.FindAsync(entity.Id); + person.ShouldBeNull(); + } + } + + [Fact] + public async Task DeleteManyAsync_WithId() + { + var entities = await PersonRepository.GetListAsync(); + + var ids = entities.Select(s => s.Id).ToArray(); + + await PersonRepository.DeleteManyAsync(ids); + + foreach (var id in ids) + { + var person = await PersonRepository.FindAsync(id); + person.ShouldBeNull(); + } + } } } From 2d2f3cb6cb4f2e34b4941afd5804042e396ff36a Mon Sep 17 00:00:00 2001 From: enisn Date: Wed, 16 Dec 2020 16:26:49 +0300 Subject: [PATCH 12/19] Docs for Bulk Operations --- docs/en/Repositories.md | 82 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/docs/en/Repositories.md b/docs/en/Repositories.md index 5ff8372af8..1162e6a248 100644 --- a/docs/en/Repositories.md +++ b/docs/en/Repositories.md @@ -87,6 +87,88 @@ If your entity is a soft-delete entity, you can use the `HardDeleteAsync` method See the [Data Filtering](Data-Filtering.md) documentation for more about soft-delete. +## Bulk Operations +You can execute bulk operations with `InsertMany`, `UpdateMany`, `DeleteMany` methods. + +Both providers `MongoDb` and `Ef Core` support bulk operations. + +### Customization + +If you have better logic or using an external library for bulk operations, you can override the logic via implementing `IMongoDbBulkOperationProvider` for MongoDb and `IEfCoreBulkOperationProvider` for Ef Core. + +- MongoDb Customization: + +```csharp +public class MyCustomMongoDbBulkOperationProvider : IMongoDbBulkOperationProvider, ITransientDependency +{ + public async Task DeleteManyAsync(IMongoDbRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TEntity : class, IEntity + { + // Your logic here. + } + + public async Task InsertManyAsync(IMongoDbRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TEntity : class, IEntity + { + // Your logic here. + } + + public async Task UpdateManyAsync(IMongoDbRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TEntity : class, IEntity + { + // Your logic here. + } +} +``` + +- Entitiy Framework Core Customization +```csharp +public class MyCustomEfCoreBulkOperationProvider : IEfCoreBulkOperationProvider, ITransientDependency +{ + public async Task DeleteManyAsync(IEfCoreRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TDbContext : IEfCoreDbContext + where TEntity : class, IEntity + { + // Your logic here. + } + + public async Task InsertManyAsync(IEfCoreRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TDbContext : IEfCoreDbContext + where TEntity : class, IEntity + { + // Your logic here. + } + + public async Task UpdateManyAsync(IEfCoreRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TDbContext : IEfCoreDbContext + where TEntity : class, IEntity + { + // Your logic here. + } +} +``` + + + + ## Custom Repositories Default generic repositories will be sufficient for most cases. However, you may need to create a custom repository class for your entity. From b09b1d343694daeb70022f809bf0881f1cc219d3 Mon Sep 17 00:00:00 2001 From: enisn Date: Wed, 16 Dec 2020 19:08:55 +0300 Subject: [PATCH 13/19] Add ConcurrencyStamp warning to Repositories.md --- docs/en/Repositories.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Repositories.md b/docs/en/Repositories.md index 1162e6a248..0920a293fa 100644 --- a/docs/en/Repositories.md +++ b/docs/en/Repositories.md @@ -166,7 +166,7 @@ public class MyCustomEfCoreBulkOperationProvider : IEfCoreBulkOperationProvider, } ``` - +> **WARNING:** ConcurrencyStamp can't be checked at bulk operations! ## Custom Repositories From 2c0b11336ce7e9429dbbbba62f209878a3d26769 Mon Sep 17 00:00:00 2001 From: enisn Date: Thu, 17 Dec 2020 13:36:20 +0300 Subject: [PATCH 14/19] Update BulkOperations implementation as xxRange methods for EfCoreRepository --- .../EntityFrameworkCore/EfCoreRepository.cs | 114 +++++++++++------- 1 file changed, 70 insertions(+), 44 deletions(-) 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 4866b9daea..437e32ad40 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 @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using Nito.AsyncEx; using System; using System.Collections.Generic; using System.Linq; @@ -62,6 +63,32 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore return savedEntity; } + public override async Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + foreach (var entity in entities) + { + CheckAndSetId(entity); + } + + if (BulkOperationProvider != null) + { + await BulkOperationProvider.InsertManyAsync( + this, + entities, + autoSave, + cancellationToken + ); + return; + } + + await DbSet.AddRangeAsync(entities); + + if (autoSave) + { + await DbContext.SaveChangesAsync(); + } + } + public async override Task UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) { DbContext.Attach(entity); @@ -76,6 +103,28 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore return updatedEntity; } + public override async Task UpdateManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + if (BulkOperationProvider != null) + { + await BulkOperationProvider.UpdateManyAsync( + this, + entities, + autoSave, + cancellationToken + ); + + return; + } + + DbSet.UpdateRange(entities); + + if (autoSave) + { + await DbContext.SaveChangesAsync(); + } + } + public async override Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) { DbSet.Remove(entity); @@ -86,6 +135,27 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore } } + public override async Task DeleteManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) + { + if (BulkOperationProvider != null) + { + await BulkOperationProvider.DeleteManyAsync( + this, + entities, + autoSave, + cancellationToken); + + return; + } + + DbSet.RemoveRange(entities); + + if (autoSave) + { + await DbContext.SaveChangesAsync(); + } + } + public async override Task> GetListAsync(bool includeDetails = false, CancellationToken cancellationToken = default) { return includeDetails @@ -113,50 +183,6 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore .ToListAsync(GetCancellationToken(cancellationToken)); } - public override Task InsertManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) - { - if (BulkOperationProvider != null) - { - return BulkOperationProvider.InsertManyAsync( - this, - entities, - autoSave, - cancellationToken - ); - } - - return base.InsertManyAsync(entities, autoSave, cancellationToken); - } - - public override Task UpdateManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) - { - if (BulkOperationProvider != null) - { - return BulkOperationProvider.UpdateManyAsync( - this, - entities, - autoSave, - cancellationToken - ); - } - - return base.UpdateManyAsync(entities, autoSave, cancellationToken); - } - - public override Task DeleteManyAsync(IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default) - { - if (BulkOperationProvider != null) - { - return BulkOperationProvider.DeleteManyAsync( - this, - entities, - autoSave, - cancellationToken); - } - - return base.DeleteManyAsync(entities, autoSave, cancellationToken); - } - protected override IQueryable GetQueryable() { return DbSet.AsQueryable(); From 6be6470f1b26637077e610fd42d6a1accb227b72 Mon Sep 17 00:00:00 2001 From: enisn Date: Mon, 21 Dec 2020 09:49:10 +0300 Subject: [PATCH 15/19] Docs Update for Bulk Operations --- docs/en/Entity-Framework-Core.md | 41 +++++++++++++++++ docs/en/MongoDB.md | 39 ++++++++++++++++ docs/en/Repositories.md | 79 +------------------------------- 3 files changed, 81 insertions(+), 78 deletions(-) diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index 8c0a1c9cc9..b2b216397a 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -735,6 +735,47 @@ Configure(options => }); ```` +### Customize Bulk Operations + +If you have better logic or using an external library for bulk operations, you can override the logic via implementing`IEfCoreBulkOperationProvider`. + +- You may use example template below: + +```csharp +public class MyCustomEfCoreBulkOperationProvider : IEfCoreBulkOperationProvider, ITransientDependency +{ + public async Task DeleteManyAsync(IEfCoreRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TDbContext : IEfCoreDbContext + where TEntity : class, IEntity + { + // Your logic here. + } + + public async Task InsertManyAsync(IEfCoreRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TDbContext : IEfCoreDbContext + where TEntity : class, IEntity + { + // Your logic here. + } + + public async Task UpdateManyAsync(IEfCoreRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TDbContext : IEfCoreDbContext + where TEntity : class, IEntity + { + // Your logic here. + } +} +``` + ## See Also * [Entities](Entities.md) diff --git a/docs/en/MongoDB.md b/docs/en/MongoDB.md index 98769c13b8..92a6168e0a 100644 --- a/docs/en/MongoDB.md +++ b/docs/en/MongoDB.md @@ -382,3 +382,42 @@ context.Services.AddMongoDbContext(options => ``` In this example, `OtherMongoDbContext` implements `IBookStoreMongoDbContext`. This feature allows you to have multiple MongoDbContext (one per module) on development, but single MongoDbContext (implements all interfaces of all MongoDbContexts) on runtime. + +### Customize Bulk Operations + + +If you have better logic or using an external library for bulk operations, you can override the logic via implementing `IMongoDbBulkOperationProvider`. + +- You may use example template below: + +```csharp +public class MyCustomMongoDbBulkOperationProvider : IMongoDbBulkOperationProvider, ITransientDependency +{ + public async Task DeleteManyAsync(IMongoDbRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TEntity : class, IEntity + { + // Your logic here. + } + + public async Task InsertManyAsync(IMongoDbRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TEntity : class, IEntity + { + // Your logic here. + } + + public async Task UpdateManyAsync(IMongoDbRepository repository, + IEnumerable entities, + bool autoSave, + CancellationToken cancellationToken) + where TEntity : class, IEntity + { + // Your logic here. + } +} +``` \ No newline at end of file diff --git a/docs/en/Repositories.md b/docs/en/Repositories.md index 0920a293fa..5ad3107ca6 100644 --- a/docs/en/Repositories.md +++ b/docs/en/Repositories.md @@ -88,87 +88,10 @@ If your entity is a soft-delete entity, you can use the `HardDeleteAsync` method See the [Data Filtering](Data-Filtering.md) documentation for more about soft-delete. ## Bulk Operations -You can execute bulk operations with `InsertMany`, `UpdateMany`, `DeleteMany` methods. - -Both providers `MongoDb` and `Ef Core` support bulk operations. - -### Customization - -If you have better logic or using an external library for bulk operations, you can override the logic via implementing `IMongoDbBulkOperationProvider` for MongoDb and `IEfCoreBulkOperationProvider` for Ef Core. - -- MongoDb Customization: - -```csharp -public class MyCustomMongoDbBulkOperationProvider : IMongoDbBulkOperationProvider, ITransientDependency -{ - public async Task DeleteManyAsync(IMongoDbRepository repository, - IEnumerable entities, - bool autoSave, - CancellationToken cancellationToken) - where TEntity : class, IEntity - { - // Your logic here. - } - - public async Task InsertManyAsync(IMongoDbRepository repository, - IEnumerable entities, - bool autoSave, - CancellationToken cancellationToken) - where TEntity : class, IEntity - { - // Your logic here. - } - - public async Task UpdateManyAsync(IMongoDbRepository repository, - IEnumerable entities, - bool autoSave, - CancellationToken cancellationToken) - where TEntity : class, IEntity - { - // Your logic here. - } -} -``` - -- Entitiy Framework Core Customization -```csharp -public class MyCustomEfCoreBulkOperationProvider : IEfCoreBulkOperationProvider, ITransientDependency -{ - public async Task DeleteManyAsync(IEfCoreRepository repository, - IEnumerable entities, - bool autoSave, - CancellationToken cancellationToken) - where TDbContext : IEfCoreDbContext - where TEntity : class, IEntity - { - // Your logic here. - } - - public async Task InsertManyAsync(IEfCoreRepository repository, - IEnumerable entities, - bool autoSave, - CancellationToken cancellationToken) - where TDbContext : IEfCoreDbContext - where TEntity : class, IEntity - { - // Your logic here. - } - - public async Task UpdateManyAsync(IEfCoreRepository repository, - IEnumerable entities, - bool autoSave, - CancellationToken cancellationToken) - where TDbContext : IEfCoreDbContext - where TEntity : class, IEntity - { - // Your logic here. - } -} -``` +You can execute bulk operations with `InsertManyAsync`, `UpdateManyAsync`, `DeleteManyAsync` methods. > **WARNING:** ConcurrencyStamp can't be checked at bulk operations! - ## Custom Repositories Default generic repositories will be sufficient for most cases. However, you may need to create a custom repository class for your entity. From a96de62437def693514d44189b49142c2e6afd60 Mon Sep 17 00:00:00 2001 From: enisn Date: Mon, 21 Dec 2020 09:52:00 +0300 Subject: [PATCH 16/19] Formatting Fo MongoDB.md --- docs/en/MongoDB.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/en/MongoDB.md b/docs/en/MongoDB.md index 92a6168e0a..1f269e94cb 100644 --- a/docs/en/MongoDB.md +++ b/docs/en/MongoDB.md @@ -385,7 +385,6 @@ In this example, `OtherMongoDbContext` implements `IBookStoreMongoDbContext`. Th ### Customize Bulk Operations - If you have better logic or using an external library for bulk operations, you can override the logic via implementing `IMongoDbBulkOperationProvider`. - You may use example template below: From 4e083ad2d44496c06ef847347c0385236443be34 Mon Sep 17 00:00:00 2001 From: enisn Date: Mon, 21 Dec 2020 10:00:52 +0300 Subject: [PATCH 17/19] Refactor of Bulk Operations Implementat'on --- .../Domain/Repositories/IBasicRepository.cs | 2 +- .../EntityFrameworkCore/EfCoreRepository.cs | 12 +++--------- .../MemoryDb/MemoryDbRepository.cs | 11 ++--------- .../MongoDB/IMongoDbBulkOperationProvider.cs | 2 -- .../MongoDB/IMongoDbRepositoryFilterer.cs | 4 ++-- .../Repositories/MongoDB/MongoDbRepository.cs | 18 ++++++++---------- 6 files changed, 16 insertions(+), 33 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs index 06a222cda7..331001fcb1 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs @@ -54,8 +54,8 @@ namespace Volo.Abp.Domain.Repositories /// This is useful for ORMs / database APIs those only save changes with an explicit method call, but you need to immediately save changes to the database. /// A to observe while waiting for the task to complete. /// Awaitable . - Task UpdateManyAsync([NotNull] IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default); + /// /// Deletes an entity. /// 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 437e32ad40..ca885bd4fb 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 @@ -344,17 +344,11 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore await DeleteAsync(entity, autoSave, cancellationToken); } - public async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) + public async virtual Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) { - foreach (var id in ids) - { - await DeleteAsync(id, cancellationToken: cancellationToken); - } + var entities = await DbSet.Where(x => ids.Contains(x.Id)).ToListAsync(); - if (autoSave) - { - await SaveChangesAsync(cancellationToken); - } + await DeleteManyAsync(entities, autoSave, cancellationToken); } } } 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 63203d8b0f..7c67e254ee 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 @@ -313,15 +313,8 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb public virtual async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) { - foreach (var id in ids) - { - await DeleteAsync(id, cancellationToken: cancellationToken); - } - - if (autoSave) - { - await SaveChangesAsync(cancellationToken); - } + var entities = await AsyncExecuter.ToListAsync(GetQueryable().Where(x => ids.Contains(x.Id))); + DeleteManyAsync(entities, autoSave, cancellationToken); } } } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs index 367a70bb86..1436a7bab7 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs @@ -16,7 +16,6 @@ namespace Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB ) where TEntity : class, IEntity; - Task UpdateManyAsync( IMongoDbRepository repository, IEnumerable entities, @@ -25,7 +24,6 @@ namespace Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB ) where TEntity : class, IEntity; - Task DeleteManyAsync( IMongoDbRepository repository, IEnumerable entities, diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs index 5a96a29022..dfabd8dde3 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs @@ -16,7 +16,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null); /// - /// Creates 'In' filter for mongoDb. + /// Creates filter for given entities. /// /// /// Visit https://docs.mongodb.com/manual/reference/operator/query/in/ to get more information about 'in' operator. @@ -27,7 +27,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB FilterDefinition CreateEntitiesFilter(IEnumerable entities, bool applyFilters = false); /// - /// Creates 'In' filter for mongoDb. + /// Creates filter for given ids. /// /// /// Visit https://docs.mongodb.com/manual/reference/operator/query/in/ to get more information about 'in' operator. 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 7c910d350f..f24c553509 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 @@ -195,27 +195,25 @@ namespace Volo.Abp.Domain.Repositories.MongoDB var entitiesCount = entities.Count(); BulkWriteResult result; + List> replaceRequests = new List>(); + foreach (var entity in entities) + { + replaceRequests.Add(new ReplaceOneModel(CreateEntityFilter(entity), entity)); + } + if (SessionHandle != null) { - result = await Collection.BulkWriteAsync(SessionHandle, GetReplaceRequests()); + result = await Collection.BulkWriteAsync(SessionHandle, replaceRequests); } else { - result = await Collection.BulkWriteAsync(GetReplaceRequests()); + result = await Collection.BulkWriteAsync(replaceRequests); } if (result.MatchedCount < entitiesCount) { ThrowOptimisticConcurrencyException(); } - - IEnumerable> GetReplaceRequests() - { - foreach (var entity in entities) - { - yield return new ReplaceOneModel(CreateEntityFilter(entity), entity); - } - } } public async override Task DeleteAsync( From f37229857c35cb3b366c1eb06fc82067b7b00555 Mon Sep 17 00:00:00 2001 From: enisn Date: Mon, 21 Dec 2020 10:00:52 +0300 Subject: [PATCH 18/19] Refactor of Bulk Operations Implementating --- .../Domain/Repositories/IBasicRepository.cs | 2 +- .../EntityFrameworkCore/EfCoreRepository.cs | 12 +++--------- .../MemoryDb/MemoryDbRepository.cs | 11 ++--------- .../MongoDB/IMongoDbBulkOperationProvider.cs | 2 -- .../MongoDB/IMongoDbRepositoryFilterer.cs | 4 ++-- .../Repositories/MongoDB/MongoDbRepository.cs | 18 ++++++++---------- 6 files changed, 16 insertions(+), 33 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs index 06a222cda7..331001fcb1 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/IBasicRepository.cs @@ -54,8 +54,8 @@ namespace Volo.Abp.Domain.Repositories /// This is useful for ORMs / database APIs those only save changes with an explicit method call, but you need to immediately save changes to the database. /// A to observe while waiting for the task to complete. /// Awaitable . - Task UpdateManyAsync([NotNull] IEnumerable entities, bool autoSave = false, CancellationToken cancellationToken = default); + /// /// Deletes an entity. /// 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 437e32ad40..ca885bd4fb 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 @@ -344,17 +344,11 @@ namespace Volo.Abp.Domain.Repositories.EntityFrameworkCore await DeleteAsync(entity, autoSave, cancellationToken); } - public async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) + public async virtual Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) { - foreach (var id in ids) - { - await DeleteAsync(id, cancellationToken: cancellationToken); - } + var entities = await DbSet.Where(x => ids.Contains(x.Id)).ToListAsync(); - if (autoSave) - { - await SaveChangesAsync(cancellationToken); - } + await DeleteManyAsync(entities, autoSave, cancellationToken); } } } 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 63203d8b0f..7c67e254ee 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 @@ -313,15 +313,8 @@ namespace Volo.Abp.Domain.Repositories.MemoryDb public virtual async Task DeleteManyAsync([NotNull] IEnumerable ids, bool autoSave = false, CancellationToken cancellationToken = default) { - foreach (var id in ids) - { - await DeleteAsync(id, cancellationToken: cancellationToken); - } - - if (autoSave) - { - await SaveChangesAsync(cancellationToken); - } + var entities = await AsyncExecuter.ToListAsync(GetQueryable().Where(x => ids.Contains(x.Id))); + DeleteManyAsync(entities, autoSave, cancellationToken); } } } diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs index 367a70bb86..1436a7bab7 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs @@ -16,7 +16,6 @@ namespace Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB ) where TEntity : class, IEntity; - Task UpdateManyAsync( IMongoDbRepository repository, IEnumerable entities, @@ -25,7 +24,6 @@ namespace Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB ) where TEntity : class, IEntity; - Task DeleteManyAsync( IMongoDbRepository repository, IEnumerable entities, diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs index 5a96a29022..dfabd8dde3 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbRepositoryFilterer.cs @@ -16,7 +16,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB FilterDefinition CreateEntityFilter(TEntity entity, bool withConcurrencyStamp = false, string concurrencyStamp = null); /// - /// Creates 'In' filter for mongoDb. + /// Creates filter for given entities. /// /// /// Visit https://docs.mongodb.com/manual/reference/operator/query/in/ to get more information about 'in' operator. @@ -27,7 +27,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB FilterDefinition CreateEntitiesFilter(IEnumerable entities, bool applyFilters = false); /// - /// Creates 'In' filter for mongoDb. + /// Creates filter for given ids. /// /// /// Visit https://docs.mongodb.com/manual/reference/operator/query/in/ to get more information about 'in' operator. 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 7c910d350f..f24c553509 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 @@ -195,27 +195,25 @@ namespace Volo.Abp.Domain.Repositories.MongoDB var entitiesCount = entities.Count(); BulkWriteResult result; + List> replaceRequests = new List>(); + foreach (var entity in entities) + { + replaceRequests.Add(new ReplaceOneModel(CreateEntityFilter(entity), entity)); + } + if (SessionHandle != null) { - result = await Collection.BulkWriteAsync(SessionHandle, GetReplaceRequests()); + result = await Collection.BulkWriteAsync(SessionHandle, replaceRequests); } else { - result = await Collection.BulkWriteAsync(GetReplaceRequests()); + result = await Collection.BulkWriteAsync(replaceRequests); } if (result.MatchedCount < entitiesCount) { ThrowOptimisticConcurrencyException(); } - - IEnumerable> GetReplaceRequests() - { - foreach (var entity in entities) - { - yield return new ReplaceOneModel(CreateEntityFilter(entity), entity); - } - } } public async override Task DeleteAsync( From 5a546124ed65a917e6840853becaf1c5ebc78e96 Mon Sep 17 00:00:00 2001 From: enisn Date: Mon, 21 Dec 2020 13:08:08 +0300 Subject: [PATCH 19/19] Consider SessionHandle for MongoDb Bulk Operations --- docs/en/MongoDB.md | 9 ++++++--- .../MongoDB/IMongoDbBulkOperationProvider.cs | 6 +++++- .../Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs | 6 +++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/en/MongoDB.md b/docs/en/MongoDB.md index 1f269e94cb..7fae9c3892 100644 --- a/docs/en/MongoDB.md +++ b/docs/en/MongoDB.md @@ -394,6 +394,7 @@ public class MyCustomMongoDbBulkOperationProvider : IMongoDbBulkOperationProvide { public async Task DeleteManyAsync(IMongoDbRepository repository, IEnumerable entities, + IClientSessionHandle sessionHandle, bool autoSave, CancellationToken cancellationToken) where TEntity : class, IEntity @@ -403,6 +404,7 @@ public class MyCustomMongoDbBulkOperationProvider : IMongoDbBulkOperationProvide public async Task InsertManyAsync(IMongoDbRepository repository, IEnumerable entities, + IClientSessionHandle sessionHandle, bool autoSave, CancellationToken cancellationToken) where TEntity : class, IEntity @@ -411,9 +413,10 @@ public class MyCustomMongoDbBulkOperationProvider : IMongoDbBulkOperationProvide } public async Task UpdateManyAsync(IMongoDbRepository repository, - IEnumerable entities, - bool autoSave, - CancellationToken cancellationToken) + IEnumerable entities, + IClientSessionHandle sessionHandle, + bool autoSave, + CancellationToken cancellationToken) where TEntity : class, IEntity { // Your logic here. diff --git a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs index 1436a7bab7..fda7f291c7 100644 --- a/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs +++ b/framework/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/IMongoDbBulkOperationProvider.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using MongoDB.Driver; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Volo.Abp.Domain.Entities; @@ -11,6 +12,7 @@ namespace Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB Task InsertManyAsync( IMongoDbRepository repository, IEnumerable entities, + IClientSessionHandle sessionHandle, bool autoSave, CancellationToken cancellationToken ) @@ -19,6 +21,7 @@ namespace Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB Task UpdateManyAsync( IMongoDbRepository repository, IEnumerable entities, + IClientSessionHandle sessionHandle, bool autoSave, CancellationToken cancellationToken ) @@ -27,6 +30,7 @@ namespace Volo.Abp.MongoDB.Volo.Abp.Domain.Repositories.MongoDB Task DeleteManyAsync( IMongoDbRepository repository, IEnumerable entities, + IClientSessionHandle sessionHandle, bool autoSave, CancellationToken cancellationToken ) 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 f24c553509..109aeac8cd 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 @@ -94,7 +94,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB if (BulkOperationProvider != null) { - await BulkOperationProvider.InsertManyAsync(this, entities, autoSave, cancellationToken); + await BulkOperationProvider.InsertManyAsync(this, entities, SessionHandle, autoSave, cancellationToken); return; } @@ -188,7 +188,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB if (BulkOperationProvider != null) { - await BulkOperationProvider.UpdateManyAsync(this, entities, autoSave, cancellationToken); + await BulkOperationProvider.UpdateManyAsync(this, entities, SessionHandle, autoSave, cancellationToken); return; } @@ -292,7 +292,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB if (BulkOperationProvider != null) { - await BulkOperationProvider.DeleteManyAsync(this, entities, autoSave, cancellationToken); + await BulkOperationProvider.DeleteManyAsync(this, entities, SessionHandle, autoSave, cancellationToken); return; }