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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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/60] 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 c14761a72309e3b30e0df4a2bb9e3c6e9543d9c3 Mon Sep 17 00:00:00 2001 From: EngincanV Date: Thu, 17 Dec 2020 16:19:18 +0300 Subject: [PATCH 15/60] Update POST.md --- .../POST.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/en/Community-Articles/2020-12-10-How-to-Integrate-the-Telerik-Blazor-Component/POST.md b/docs/en/Community-Articles/2020-12-10-How-to-Integrate-the-Telerik-Blazor-Component/POST.md index 4bdebb6234..971eb88f97 100644 --- a/docs/en/Community-Articles/2020-12-10-How-to-Integrate-the-Telerik-Blazor-Component/POST.md +++ b/docs/en/Community-Articles/2020-12-10-How-to-Integrate-the-Telerik-Blazor-Component/POST.md @@ -52,7 +52,10 @@ abp new TelerikComponents --ui blazor --database-provider ef ```html ... - + +