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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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/41] 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 b24f080b2ab1851d446ffb87ca3b31e165757531 Mon Sep 17 00:00:00 2001 From: PM Extra Date: Fri, 18 Dec 2020 18:19:55 +0800 Subject: [PATCH 15/41] Fix AbpStringExtensions For now, `"".RemovePreFix("x")` will return null. It's not a expected result. I think we shouldn't make empty string be null when trim prefix or postfix. --- framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs b/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs index 6aa3deb4f0..76ae06a5e0 100644 --- a/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs +++ b/framework/src/Volo.Abp.Core/System/AbpStringExtensions.cs @@ -133,7 +133,7 @@ namespace System { if (str.IsNullOrEmpty()) { - return null; + return str; } if (postFixes.IsNullOrEmpty()) @@ -174,7 +174,7 @@ namespace System { if (str.IsNullOrEmpty()) { - return null; + return str; } if (preFixes.IsNullOrEmpty()) From 9d1b98fd9544af060b53aa358fce166ef1f00a83 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 20 Dec 2020 10:32:28 +0800 Subject: [PATCH 16/41] Add RemovePostFix & RemovePreFix unit tests. --- .../System/StringExtensions_Tests.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/framework/test/Volo.Abp.Core.Tests/System/StringExtensions_Tests.cs b/framework/test/Volo.Abp.Core.Tests/System/StringExtensions_Tests.cs index 69b02d07c1..3a4a3746eb 100644 --- a/framework/test/Volo.Abp.Core.Tests/System/StringExtensions_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/System/StringExtensions_Tests.cs @@ -182,7 +182,10 @@ namespace System public void RemovePostFix_Tests() { //null case - (null as string).RemovePreFix("Test").ShouldBeNull(); + (null as string).RemovePostFix("Test").ShouldBeNull(); + + //empty case + string.Empty.RemovePostFix("Test").ShouldBe(string.Empty); //Simple case "MyTestAppService".RemovePostFix("AppService").ShouldBe("MyTest"); @@ -202,6 +205,12 @@ namespace System [Fact] public void RemovePreFix_Tests() { + //null case + (null as string).RemovePreFix("Test").ShouldBeNull(); + + //empty case + string.Empty.RemovePreFix("Test").ShouldBe(string.Empty); + "Home.Index".RemovePreFix("NotMatchedPostfix").ShouldBe("Home.Index"); "Home.About".RemovePreFix("Home.").ShouldBe("About"); From 92b70b95fcd089ad6baf7d2581b1007ab0863536 Mon Sep 17 00:00:00 2001 From: Berkan Date: Sun, 20 Dec 2020 14:50:58 +0300 Subject: [PATCH 17/41] Update en.json --- .../AbpIoLocalization/Community/Localization/Resources/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json index a29355b401..a92c20a991 100644 --- a/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json +++ b/abp_io/AbpIoLocalization/AbpIoLocalization/Community/Localization/Resources/en.json @@ -37,7 +37,7 @@ "CreateArticleTitleInfo": "Title of the article to be shown on the article list.", "CreateArticleUrlInfo": "Original GitHub/External URL of the article.", "CreateArticleSummaryInfo": "A short summary of the article to be shown on the article list.", - "CreateArticleCoverInfo": "For creating an effective article, add a cover photo. Upload 16:9 aspect ratio pictures for the best view.", + "CreateArticleCoverInfo": "For creating an effective article, add a cover photo. Upload 16:9 aspect ratio pictures for the best view. Maximum file size: 1MB.", "ThisExtensionIsNotAllowed": "This extension is not allowed.", "TheFileIsTooLarge": "The file is too large.", "GoToTheArticle": "Go to the Article", From a4dad2df061d61258c39df75afe55ee2f8185250 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 21 Dec 2020 09:33:24 +0800 Subject: [PATCH 18/41] Update StringExtensions_Tests.cs --- .../test/Volo.Abp.Core.Tests/System/StringExtensions_Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/test/Volo.Abp.Core.Tests/System/StringExtensions_Tests.cs b/framework/test/Volo.Abp.Core.Tests/System/StringExtensions_Tests.cs index 3a4a3746eb..5f4e459ebe 100644 --- a/framework/test/Volo.Abp.Core.Tests/System/StringExtensions_Tests.cs +++ b/framework/test/Volo.Abp.Core.Tests/System/StringExtensions_Tests.cs @@ -211,7 +211,7 @@ namespace System //empty case string.Empty.RemovePreFix("Test").ShouldBe(string.Empty); - "Home.Index".RemovePreFix("NotMatchedPostfix").ShouldBe("Home.Index"); + "Home.Index".RemovePreFix("NotMatchedPrefix").ShouldBe("Home.Index"); "Home.About".RemovePreFix("Home.").ShouldBe("About"); //Ignore case From 6be6470f1b26637077e610fd42d6a1accb227b72 Mon Sep 17 00:00:00 2001 From: enisn Date: Mon, 21 Dec 2020 09:49:10 +0300 Subject: [PATCH 19/41] 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 20/41] 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 4aa8fb1e3fcc67b0c8f88d4406d09cfa210655da Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 21 Dec 2020 15:00:09 +0800 Subject: [PATCH 21/41] Exclude UserCache of ProxyScriptingModel as the cache key. --- .../Abp/Http/ProxyScripting/ProxyScriptManager.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/ProxyScriptManager.cs b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/ProxyScriptManager.cs index 7ac3651ad9..c619e5483c 100644 --- a/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/ProxyScriptManager.cs +++ b/framework/src/Volo.Abp.Http/Volo/Abp/Http/ProxyScripting/ProxyScriptManager.cs @@ -20,7 +20,7 @@ namespace Volo.Abp.Http.ProxyScripting private readonly AbpApiProxyScriptingOptions _options; public ProxyScriptManager( - IApiDescriptionModelProvider modelProvider, + IApiDescriptionModelProvider modelProvider, IServiceProvider serviceProvider, IJsonSerializer jsonSerializer, IProxyScriptManagerCache cache, @@ -70,7 +70,14 @@ namespace Volo.Abp.Http.ProxyScripting private string CreateCacheKey(ProxyScriptingModel model) { - return _jsonSerializer.Serialize(model).ToMd5(); + return _jsonSerializer.Serialize(new + { + model.GeneratorType, + model.Modules, + model.Controllers, + model.Actions, + model.Properties + }).ToMd5(); } } } From 4e083ad2d44496c06ef847347c0385236443be34 Mon Sep 17 00:00:00 2001 From: enisn Date: Mon, 21 Dec 2020 10:00:52 +0300 Subject: [PATCH 22/41] 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 23/41] 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 ba16c19e839e7346117327e721fa19c5364f5ad5 Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 21 Dec 2020 17:59:31 +0800 Subject: [PATCH 24/41] Add LogoUrl to Brand component. Resolve #6704 --- .../Themes/Basic/Branding.razor | 10 ++++++++-- .../Themes/Basic/Components/Brand/Default.cshtml | 8 +++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/Branding.razor b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/Branding.razor index ac629423ce..5e0537b2b2 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/Branding.razor +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/Branding.razor @@ -1,3 +1,9 @@ -@using Volo.Abp.Ui.Branding +@using Volo.Abp.Ui.Branding @inject IBrandingProvider BrandingProvider -@BrandingProvider.AppName + + @if (!BrandingProvider.LogoUrl.IsNullOrWhiteSpace()) + { + @BrandingProvider.AppName + } + @BrandingProvider.AppName + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/Default.cshtml b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/Default.cshtml index b57f6da4bb..04a4f81c71 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/Default.cshtml +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic/Themes/Basic/Components/Brand/Default.cshtml @@ -1,3 +1,9 @@ @using Volo.Abp.Ui.Branding @inject IBrandingProvider BrandingProvider -@BrandingProvider.AppName + + @if (!BrandingProvider.LogoUrl.IsNullOrWhiteSpace()) + { + @BrandingProvider.AppName + } + @BrandingProvider.AppName + From 5a546124ed65a917e6840853becaf1c5ebc78e96 Mon Sep 17 00:00:00 2001 From: enisn Date: Mon, 21 Dec 2020 13:08:08 +0300 Subject: [PATCH 25/41] 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; } From acb596d38cce409e54bfd9b63738601e8e83c22b Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Mon, 21 Dec 2020 21:05:25 +0800 Subject: [PATCH 26/41] Added RedirectAlowedUrls --- .../Mvc/UI/RazorPages/AbpPageModel.cs | 41 +++++++++++++++++++ .../Volo.Abp.AspNetCore.Mvc.csproj | 1 + .../AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs | 3 +- .../Volo/Abp/AspNetCore/Mvc/AbpController.cs | 41 +++++++++++++++++++ .../ChallengeAccountController.cs | 37 ----------------- .../Abp/Ui/Navigation/Urls/AppUrlOptions.cs | 7 +++- .../Abp/Ui/Navigation/Urls/AppUrlProvider.cs | 6 +++ .../Abp/Ui/Navigation/Urls/IAppUrlProvider.cs | 2 + .../Pages/Account/AccountPageModel.cs | 37 ----------------- .../MyProjectNameHttpApiHostModule.cs | 1 + .../appsettings.json | 3 +- .../MyProjectNameIdentityServerModule.cs | 1 + .../appsettings.json | 3 +- 13 files changed, 105 insertions(+), 78 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs index 4303c698fe..fcbe9af59c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI/Volo/Abp/AspNetCore/Mvc/UI/RazorPages/AbpPageModel.cs @@ -16,6 +16,7 @@ using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; using Volo.Abp.Settings; using Volo.Abp.Timing; +using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.Uow; using Volo.Abp.Users; @@ -125,6 +126,9 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages protected ILogger Logger => _lazyLogger.Value; private Lazy _lazyLogger => new Lazy(() => LoggerFactory?.CreateLogger(GetType().FullName) ?? NullLogger.Instance, true); + protected IAppUrlProvider AppUrlProvider => LazyGetRequiredService(ref _appUrlProvider); + private IAppUrlProvider _appUrlProvider; + protected virtual NoContentResult NoContent() //TODO: Is that true to return empty result like that? { return new NoContentResult(); @@ -165,5 +169,42 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.RazorPages return localizer; } + + protected RedirectResult RedirectSafely(string returnUrl, string returnUrlHash = null) + { + return Redirect(GetRedirectUrl(returnUrl, returnUrlHash)); + } + + protected virtual string GetRedirectUrl(string returnUrl, string returnUrlHash = null) + { + returnUrl = NormalizeReturnUrl(returnUrl); + + if (!returnUrlHash.IsNullOrWhiteSpace()) + { + returnUrl = returnUrl + returnUrlHash; + } + + return returnUrl; + } + + private string NormalizeReturnUrl(string returnUrl) + { + if (returnUrl.IsNullOrEmpty()) + { + return GetAppHomeUrl(); + } + + if (Url.IsLocalUrl(returnUrl) || AppUrlProvider.IsRedirectAllowedUrl(returnUrl)) + { + return returnUrl; + } + + return GetAppHomeUrl(); + } + + protected virtual string GetAppHomeUrl() + { + return "~/"; //TODO: ??? + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj index 53e3c7f503..1f2211c14f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj @@ -23,6 +23,7 @@ + diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs index 61d73cb48b..e5edd9662d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs @@ -39,6 +39,7 @@ using Volo.Abp.Json; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.UI; +using Volo.Abp.UI.Navigation; namespace Volo.Abp.AspNetCore.Mvc { @@ -47,7 +48,7 @@ namespace Volo.Abp.AspNetCore.Mvc typeof(AbpLocalizationModule), typeof(AbpApiVersioningAbstractionsModule), typeof(AbpAspNetCoreMvcContractsModule), - typeof(AbpUiModule), + typeof(AbpUiNavigationModule), typeof(AbpGlobalFeaturesModule) )] public class AbpAspNetCoreMvcModule : AbpModule diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs index 645e13b026..f83353092e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpController.cs @@ -14,6 +14,7 @@ using Volo.Abp.Localization; using Volo.Abp.MultiTenancy; using Volo.Abp.ObjectMapping; using Volo.Abp.Timing; +using Volo.Abp.UI.Navigation.Urls; using Volo.Abp.Uow; using Volo.Abp.Users; @@ -115,6 +116,9 @@ namespace Volo.Abp.AspNetCore.Mvc } private IStringLocalizer _localizer; + protected IAppUrlProvider AppUrlProvider => LazyGetRequiredService(ref _appUrlProvider); + private IAppUrlProvider _appUrlProvider; + protected Type LocalizationResource { get => _localizationResource; @@ -148,5 +152,42 @@ namespace Volo.Abp.AspNetCore.Mvc return localizer; } + + protected RedirectResult RedirectSafely(string returnUrl, string returnUrlHash = null) + { + return Redirect(GetRedirectUrl(returnUrl, returnUrlHash)); + } + + private string GetRedirectUrl(string returnUrl, string returnUrlHash = null) + { + returnUrl = NormalizeReturnUrl(returnUrl); + + if (!returnUrlHash.IsNullOrWhiteSpace()) + { + returnUrl = returnUrl + returnUrlHash; + } + + return returnUrl; + } + + private string NormalizeReturnUrl(string returnUrl) + { + if (returnUrl.IsNullOrEmpty()) + { + return GetAppHomeUrl(); + } + + if (Url.IsLocalUrl(returnUrl) || AppUrlProvider.IsRedirectAllowedUrl(returnUrl)) + { + return returnUrl; + } + + return GetAppHomeUrl(); + } + + protected virtual string GetAppHomeUrl() + { + return "~/"; + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs index 781866232b..d01bac2e81 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Authentication/ChallengeAccountController.cs @@ -66,42 +66,5 @@ namespace Volo.Abp.AspNetCore.Mvc.Authentication return NoContent(); } - - protected RedirectResult RedirectSafely(string returnUrl, string returnUrlHash = null) - { - return Redirect(GetRedirectUrl(returnUrl, returnUrlHash)); - } - - private string GetRedirectUrl(string returnUrl, string returnUrlHash = null) - { - returnUrl = NormalizeReturnUrl(returnUrl); - - if (!returnUrlHash.IsNullOrWhiteSpace()) - { - returnUrl = returnUrl + returnUrlHash; - } - - return returnUrl; - } - - private string NormalizeReturnUrl(string returnUrl) - { - if (returnUrl.IsNullOrEmpty()) - { - return GetAppHomeUrl(); - } - - if (Url.IsLocalUrl(returnUrl)) - { - return returnUrl; - } - - return GetAppHomeUrl(); - } - - protected virtual string GetAppHomeUrl() - { - return "~/"; - } } } diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlOptions.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlOptions.cs index ebfbbedcd5..79a1afba14 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlOptions.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlOptions.cs @@ -1,12 +1,17 @@ -namespace Volo.Abp.UI.Navigation.Urls +using System.Collections.Generic; + +namespace Volo.Abp.UI.Navigation.Urls { public class AppUrlOptions { public ApplicationUrlDictionary Applications { get; } + public List RedirectAllowedUrls { get; } + public AppUrlOptions() { Applications = new ApplicationUrlDictionary(); + RedirectAllowedUrls = new List(); } } } diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs index 912621c424..62212c2206 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; @@ -36,6 +37,11 @@ namespace Volo.Abp.UI.Navigation.Urls ); } + public bool IsRedirectAllowedUrl(string url) + { + return Options.RedirectAllowedUrls.Any(url.Contains); + } + protected virtual Task GetConfiguredUrl(string appName, string urlName) { var app = Options.Applications[appName]; diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs index 37fe1ea0de..61b6fc2bac 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/IAppUrlProvider.cs @@ -6,5 +6,7 @@ namespace Volo.Abp.UI.Navigation.Urls public interface IAppUrlProvider { Task GetUrlAsync([NotNull] string appName, [CanBeNull] string urlName = null); + + bool IsRedirectAllowedUrl(string url); } } diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs index 36564f9af8..3e016c8bb5 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/AccountPageModel.cs @@ -25,11 +25,6 @@ namespace Volo.Abp.Account.Web.Pages.Account ObjectMapperContext = typeof(AbpAccountWebModule); } - protected virtual RedirectResult RedirectSafely(string returnUrl, string returnUrlHash = null) - { - return Redirect(GetRedirectUrl(returnUrl, returnUrlHash)); - } - protected virtual void CheckIdentityErrors(IdentityResult identityResult) { if (!identityResult.Succeeded) @@ -40,33 +35,6 @@ namespace Volo.Abp.Account.Web.Pages.Account //identityResult.CheckErrors(LocalizationManager); //TODO: Get from old Abp } - protected virtual string GetRedirectUrl(string returnUrl, string returnUrlHash = null) - { - returnUrl = NormalizeReturnUrl(returnUrl); - - if (!returnUrlHash.IsNullOrWhiteSpace()) - { - returnUrl = returnUrl + returnUrlHash; - } - - return returnUrl; - } - - protected virtual string NormalizeReturnUrl(string returnUrl) - { - if (returnUrl.IsNullOrEmpty()) - { - return GetAppHomeUrl(); - } - - if (Url.IsLocalUrl(returnUrl)) - { - return returnUrl; - } - - return GetAppHomeUrl(); - } - protected virtual void CheckCurrentTenant(Guid? tenantId) { if (CurrentTenant.Id != tenantId) @@ -74,10 +42,5 @@ namespace Volo.Abp.Account.Web.Pages.Account throw new ApplicationException($"Current tenant is different than given tenant. CurrentTenant.Id: {CurrentTenant.Id}, given tenantId: {tenantId}"); } } - - protected virtual string GetAppHomeUrl() - { - return "~/"; //TODO: ??? - } } } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs index 9551cad310..39cedbb06a 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/MyProjectNameHttpApiHostModule.cs @@ -77,6 +77,7 @@ namespace MyCompanyName.MyProjectName Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; + options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); }); } diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/appsettings.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/appsettings.json index e62c32aa0c..795753ec9d 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/appsettings.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.HttpApi.HostWithIds/appsettings.json @@ -1,7 +1,8 @@ { "App": { "SelfUrl": "https://localhost:44305", - "CorsOrigins": "https://*.MyProjectName.com,http://localhost:4200,https://localhost:44307" + "CorsOrigins": "https://*.MyProjectName.com,http://localhost:4200,https://localhost:44307", + "RedirectAllowedUrls": "http://localhost:4200,https://localhost:44307" }, "ConnectionStrings": { "Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true" diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs index a5013823ac..07db333488 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/MyProjectNameIdentityServerModule.cs @@ -112,6 +112,7 @@ namespace MyCompanyName.MyProjectName Configure(options => { options.Applications["MVC"].RootUrl = configuration["App:SelfUrl"]; + options.RedirectAllowedUrls.AddRange(configuration["App:RedirectAllowedUrls"].Split(',')); }); Configure(options => diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/appsettings.json b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/appsettings.json index e9152eb6ad..c7101f92ed 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/appsettings.json +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.IdentityServer/appsettings.json @@ -1,7 +1,8 @@ { "App": { "SelfUrl": "https://localhost:44301", - "CorsOrigins": "https://*.MyProjectName.com,http://localhost:4200,https://localhost:44307,https://localhost:44300" + "CorsOrigins": "https://*.MyProjectName.com,http://localhost:4200,https://localhost:44307,https://localhost:44300", + "RedirectAllowedUrls": "http://localhost:4200,https://localhost:44307" }, "ConnectionStrings": { "Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=MyProjectName;Trusted_Connection=True;MultipleActiveResultSets=true" From 5c68affedb5a1571ae405f02a787ef6339171185 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 22 Dec 2020 11:47:15 +0800 Subject: [PATCH 27/41] Improved permission management modal performance --- .../PermissionManagementModal.cshtml | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml index dd2c73eb23..f9de308033 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml @@ -20,25 +20,28 @@ @for (var i = 0; i < Model.Groups.Count; i++) { - -

@Model.Groups[i].DisplayName

+ var group = Model.Groups[i]; + +

@group.DisplayName


-
- @for (var j = 0; j < Model.Groups[i].Permissions.Count; j++) + @for (var j = 0; j < group.Permissions.Count; j++) { - - + var permission = group.Permissions[j]; + + }
From 01a84b3a38e585ad9878a161cfd4abb253b09806 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 22 Dec 2020 13:39:25 +0800 Subject: [PATCH 28/41] Update PermissionManagementModal --- .../AbpPermissionManagement/PermissionManagementModal.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml index f9de308033..ff8f96c890 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml @@ -41,7 +41,7 @@ group-data-permission-name="@permission.Name" group-data-parent-name="@(permission.ParentName ?? "")" group-style="margin-left: @(permission.Depth * 20)px"/> - + } From aef5ffc2590a720ba4123b2fbd88341867005379 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 22 Dec 2020 13:42:44 +0800 Subject: [PATCH 29/41] Update PermissionManagementModal --- .../AbpPermissionManagement/PermissionManagementModal.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml index ff8f96c890..1f9ab1ab91 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml @@ -41,7 +41,7 @@ group-data-permission-name="@permission.Name" group-data-parent-name="@(permission.ParentName ?? "")" group-style="margin-left: @(permission.Depth * 20)px"/> - + } From 863b3f936fd5ce2db45dba7e78dcdbe3b29e047e Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 22 Dec 2020 15:35:43 +0800 Subject: [PATCH 30/41] Update PermissionManagementModal --- .../AbpPermissionManagement/PermissionManagementModal.cshtml | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml index 1f9ab1ab91..661c50677b 100644 --- a/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml +++ b/modules/permission-management/src/Volo.Abp.PermissionManagement.Web/Pages/AbpPermissionManagement/PermissionManagementModal.cshtml @@ -35,6 +35,7 @@ { var permission = group.Permissions[j]; Date: Tue, 22 Dec 2020 17:10:42 +0800 Subject: [PATCH 31/41] Avoid using array index in taghelper expressions --- .../Pages/Consent.cshtml | 30 ++++++++++--------- .../FeatureManagementModal.cshtml | 14 +++++---- .../Pages/Identity/Users/CreateModal.cshtml | 5 ++-- .../Pages/Identity/Users/EditModal.cshtml | 7 +++-- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml index 8fb7e918e1..02b4f75416 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml @@ -32,22 +32,23 @@
    @for (var i = 0; i < Model.ConsentInput.IdentityScopes.Count; i++) { + var identityScope = Model.ConsentInput.IdentityScopes[i];
  • -
    - @* TODO: Use attributes on the view model instead of using hidden here *@ - @if (Model.ConsentInput.IdentityScopes[i].Description != null) + @* TODO: Use attributes on the view model instead of using hidden here *@ + @if (identityScope.Description != null) { }
  • @@ -62,22 +63,23 @@
      @for (var i = 0; i < Model.ConsentInput.ApiScopes.Count; i++) { + var apiScope = Model.ConsentInput.ApiScopes[i];
    • -
      - @* TODO: Use attributes on the view model instead of using hidden here *@ - @if (Model.ConsentInput.ApiScopes[i].Description != null) + @* TODO: Use attributes on the view model instead of using hidden here *@ + @if (apiScope.Description != null) { }
    • diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml index a5592c2f28..f3080b7275 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Pages/FeatureManagement/FeatureManagementModal.cshtml @@ -32,14 +32,15 @@ @for (var i = 0; i < featureGroups.Count; i++) { - -

      @featureGroups[i].DisplayName

      + var featureGroup = featureGroups[i]; + +

      @featureGroup.DisplayName


      - @for (var j = 0; j < featureGroups[i].Features.Count; j++) + @for (var j = 0; j < featureGroup.Features.Count; j++) { - var feature = featureGroups[i].Features[j]; + var feature = featureGroup.Features[j]; var disabled = Model.IsDisabled(feature.Provider.Name);
      @@ -48,7 +49,7 @@ @if (feature.ValueType is ToggleStringValueType) { - @for (var i = 0; i < Model.Roles.Length; i++) { - - + var role = Model.Roles[i]; + + } diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml index cea3034cb0..ca290a33ed 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/EditModal.cshtml @@ -18,7 +18,7 @@ - + @@ -60,8 +60,9 @@ @for (var i = 0; i < Model.Roles.Length; i++) { - - + var role = Model.Roles[i]; + + } From 7bd51048aa309ae3fadbef1128c68a020c8a95b6 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Tue, 22 Dec 2020 20:36:54 +0800 Subject: [PATCH 32/41] Use AbpIdNameTagHelper --- .../Pages/Consent.cshtml | 8 ++++---- .../Pages/Identity/Users/CreateModal.cshtml | 4 ++-- .../Pages/Identity/Users/EditModal.cshtml | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml index 02b4f75416..47459bc81b 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Consent.cshtml @@ -36,7 +36,7 @@
    • - @* TODO: Use attributes on the view model instead of using hidden here *@ + @* TODO: Use attributes on the view model instead of using hidden here *@ @if (identityScope.Description != null) {
    • - @* TODO: Use attributes on the view model instead of using hidden here *@ + @* TODO: Use attributes on the view model instead of using hidden here *@ @if (apiScope.Description != null) {
    • From 890869d1bbeb071935d47d32306f2820a37cdf6a Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 23 Dec 2020 10:06:39 +0800 Subject: [PATCH 34/41] Localize the submit button text. Resolve #6769 --- .../TagHelpers/Form/AbpDynamicformTagHelperService.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs index cd8c7e6f22..da622b968b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bootstrap/TagHelpers/Form/AbpDynamicformTagHelperService.cs @@ -4,11 +4,13 @@ using System.Linq; using System.Text; using System.Text.Encodings.Web; using System.Threading.Tasks; +using Localization.Resources.AbpUi; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.TagHelpers; using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Razor.TagHelpers; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Localization; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.Microsoft.AspNetCore.Razor.TagHelpers; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Button; using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Extensions; @@ -20,15 +22,18 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form private readonly HtmlEncoder _htmlEncoder; private readonly IHtmlGenerator _htmlGenerator; private readonly IServiceProvider _serviceProvider; + private readonly IStringLocalizer _localizer; public AbpDynamicFormTagHelperService( HtmlEncoder htmlEncoder, IHtmlGenerator htmlGenerator, - IServiceProvider serviceProvider) + IServiceProvider serviceProvider, + IStringLocalizer localizer) { _htmlEncoder = htmlEncoder; _htmlGenerator = htmlGenerator; _serviceProvider = serviceProvider; + _localizer = localizer; } public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output) @@ -191,7 +196,7 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form { var abpButtonTagHelper = _serviceProvider.GetRequiredService(); var attributes = new TagHelperAttributeList { new TagHelperAttribute("type", "submit") }; - abpButtonTagHelper.Text = "Submit"; + abpButtonTagHelper.Text = _localizer["Submit"]; abpButtonTagHelper.ButtonType = AbpButtonType.Primary; return await abpButtonTagHelper.RenderAsync(attributes, context, _htmlEncoder, "button", TagMode.StartTagAndEndTag); From 8f077639f47141757ce0503a765fa27746f4727e Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 23 Dec 2020 13:37:58 +0800 Subject: [PATCH 35/41] Add more http test to JMeter. Resolve #6768 --- test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx | 173 +++++++++++++----- .../_jmeter/SimpleTestPlanWithoutAbp.jmx | 171 ++++++++++++----- 2 files changed, 249 insertions(+), 95 deletions(-) diff --git a/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx b/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx index e532423147..ea7ea15bde 100644 --- a/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx +++ b/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx @@ -1,5 +1,5 @@ - + @@ -26,15 +26,54 @@ true - - + + true + + + + false + { + "name": "${__RandomString(5, abcdefghijklmnopqrstuvwxyz)}", + "price": ${__Random(1,999)}, + "isAvailable": true +} + + = + + + + localhost + 5000 + https + + /api/books/ + POST + true + false + true + false + + + + + + + BookId + $ + + + + + + + localhost - 5001 + 5000 https - /api/books + api/books/${BookId} GET true false @@ -45,16 +84,16 @@ - + true false { - "name": "My book", - "price": "33", - "isAvailable": "true" + "name": "${__RandomString(5, abcdefghijklmnopqrstuvwxyz)}", + "price": ${__Random(1,999)}, + "isAvailable": true } = @@ -62,11 +101,30 @@ localhost - 5001 + 5000 https - /api/books/ - POST + /api/books/${BookId} + PUT + true + false + true + false + + + + + + + + + + localhost + 5000 + https + + /api/books + GET true false true @@ -76,44 +134,63 @@ - - false - - saveConfig - - - true - true - true - - true - true - true - true - false - true - true - false - false - false - true - false - false - false - true - 0 - true - true - true - true - true - true - - - - + + + + + localhost + 5000 + https + + /api/books/${BookId} + DELETE + true + false + true + false + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + diff --git a/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx b/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx index 05dbaeacf8..f51d9c0237 100644 --- a/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx +++ b/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx @@ -1,5 +1,5 @@ - + @@ -26,15 +26,54 @@ true - - + + true + + + + false + { + "name": "${__RandomString(5, abcdefghijklmnopqrstuvwxyz)}", + "price": ${__Random(1,999)}, + "isAvailable": true +} + + = + + + + localhost + 5001 + https + + /api/books/ + POST + true + false + true + false + + + + + + + BookId + $ + + + + + + + localhost - 5003 + 5001 https - /api/books + api/books/${BookId} GET true false @@ -45,15 +84,15 @@ - + true false { - "name": "My book", - "price": 33, + "name": "${__RandomString(5, abcdefghijklmnopqrstuvwxyz)}", + "price": ${__Random(1,999)}, "isAvailable": true } @@ -62,11 +101,11 @@ localhost - 5003 + 5001 https - /api/books/ - POST + /api/books/${BookId} + PUT true false true @@ -76,44 +115,82 @@ - - false - - saveConfig - - - true - true - true - - true - true - true - true - false - true - true - false - false - false - true - false - false - false - true - 0 - true - true - true - true - true - true - - - - + + + + + localhost + 5001 + https + + /api/books + GET + true + false + true + false + + + + + + + + + + localhost + 5001 + https + + /api/books/${BookId} + DELETE + true + false + true + false + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + From 1de83451472c96c4286572d171a487bd3a4ceea3 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 23 Dec 2020 13:57:54 +0800 Subject: [PATCH 36/41] JMeter User Defined Variables --- test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx | 6 +++--- test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx b/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx index ea7ea15bde..b3e353ca26 100644 --- a/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx +++ b/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx @@ -66,7 +66,7 @@ - + localhost @@ -116,7 +116,7 @@ - + localhost @@ -135,7 +135,7 @@ - + localhost diff --git a/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx b/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx index f51d9c0237..f1a5519467 100644 --- a/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx +++ b/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx @@ -66,7 +66,7 @@ - + localhost @@ -116,7 +116,7 @@ - + localhost @@ -135,7 +135,7 @@ - + localhost From f89f21164d896d2525988c7e5b7d800605802cc3 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 23 Dec 2020 14:08:01 +0800 Subject: [PATCH 37/41] Update appUrlProvider --- .../Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs index 62212c2206..c722529eca 100644 --- a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Urls/AppUrlProvider.cs @@ -39,7 +39,7 @@ namespace Volo.Abp.UI.Navigation.Urls public bool IsRedirectAllowedUrl(string url) { - return Options.RedirectAllowedUrls.Any(url.Contains); + return Options.RedirectAllowedUrls.Any(url.StartsWith); } protected virtual Task GetConfiguredUrl(string appName, string urlName) From 2e37b500c589cae3ddcdaa851111f467b35a1783 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 23 Dec 2020 14:10:44 +0800 Subject: [PATCH 38/41] Update Volo.Abp.AspNetCore.Mvc.csproj --- .../src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj index 1f2211c14f..d0806d929d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo.Abp.AspNetCore.Mvc.csproj @@ -24,7 +24,6 @@ - From 2bad56d020feddeabf6e729ba578793b1482fb4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 23 Dec 2020 10:15:02 +0300 Subject: [PATCH 39/41] Worked on test cases. --- .../AbpPerfTest/AbpPerfTest.WithAbp/AppModule.cs | 2 +- .../Controllers/BookController.cs | 3 ++- test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx | 16 ++++++++-------- .../_jmeter/SimpleTestPlanWithoutAbp.jmx | 16 ++++++++-------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/test/AbpPerfTest/AbpPerfTest.WithAbp/AppModule.cs b/test/AbpPerfTest/AbpPerfTest.WithAbp/AppModule.cs index 3846cdd3de..1cd88989e6 100644 --- a/test/AbpPerfTest/AbpPerfTest.WithAbp/AppModule.cs +++ b/test/AbpPerfTest/AbpPerfTest.WithAbp/AppModule.cs @@ -33,7 +33,7 @@ namespace AbpPerfTest.WithAbp Configure(options => { - options.TransactionBehavior = UnitOfWorkTransactionBehavior.Auto; + options.TransactionBehavior = UnitOfWorkTransactionBehavior.Disabled; }); } diff --git a/test/AbpPerfTest/AbpPerfTest.WithAbp/Controllers/BookController.cs b/test/AbpPerfTest/AbpPerfTest.WithAbp/Controllers/BookController.cs index 1533e95f71..7c78937342 100644 --- a/test/AbpPerfTest/AbpPerfTest.WithAbp/Controllers/BookController.cs +++ b/test/AbpPerfTest/AbpPerfTest.WithAbp/Controllers/BookController.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using AbpPerfTest.WithAbp.Dtos; using AbpPerfTest.WithAbp.Entities; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using Volo.Abp.Domain.Repositories; namespace AbpPerfTest.WithAbp.Controllers @@ -22,7 +23,7 @@ namespace AbpPerfTest.WithAbp.Controllers [HttpGet] public async Task> GetListAsync() { - var books = await _bookRepository.GetPagedListAsync(0, 10, "Id"); + var books = await _bookRepository.OrderBy(x => x.Id).Take(10).ToListAsync(); return books .Select(b => new BookDto diff --git a/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx b/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx index ea7ea15bde..24327e6e14 100644 --- a/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx +++ b/test/AbpPerfTest/_jmeter/SimpleTestPlan.jmx @@ -1,5 +1,5 @@ - + @@ -43,7 +43,7 @@ localhost - 5000 + 5001 https /api/books/ @@ -70,7 +70,7 @@ localhost - 5000 + 5001 https api/books/${BookId} @@ -84,7 +84,7 @@ - + true @@ -101,7 +101,7 @@ localhost - 5000 + 5001 https /api/books/${BookId} @@ -115,12 +115,12 @@ - + localhost - 5000 + 5001 https /api/books @@ -139,7 +139,7 @@ localhost - 5000 + 5001 https /api/books/${BookId} diff --git a/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx b/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx index f51d9c0237..39f188daf3 100644 --- a/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx +++ b/test/AbpPerfTest/_jmeter/SimpleTestPlanWithoutAbp.jmx @@ -1,5 +1,5 @@ - + @@ -43,7 +43,7 @@ localhost - 5001 + 5003 https /api/books/ @@ -70,7 +70,7 @@ localhost - 5001 + 5003 https api/books/${BookId} @@ -84,7 +84,7 @@ - + true @@ -101,7 +101,7 @@ localhost - 5001 + 5003 https /api/books/${BookId} @@ -115,12 +115,12 @@ - + localhost - 5001 + 5003 https /api/books @@ -139,7 +139,7 @@ localhost - 5001 + 5003 https /api/books/${BookId} From 672681502968ba7003c909c96f174a6627c8d1e4 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Wed, 23 Dec 2020 15:21:57 +0800 Subject: [PATCH 40/41] Blazor theme menu supports target --- .../Themes/Basic/FirstLevelNavMenuItem.razor | 2 +- .../Themes/Basic/LoginDisplay.razor | 9 +++++---- .../Themes/Basic/SecondLevelNavMenuItem.razor | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/FirstLevelNavMenuItem.razor b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/FirstLevelNavMenuItem.razor index cf6703ade9..b2fab0807d 100644 --- a/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/FirstLevelNavMenuItem.razor +++ b/framework/src/Volo.Abp.AspNetCore.Components.WebAssembly.BasicTheme/Themes/Basic/FirstLevelNavMenuItem.razor @@ -10,7 +10,7 @@ if (MenuItem.Url != null) {