From 35c1d810c4e4dc4b9aef2db5baf23bf474181f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 02:22:08 +0300 Subject: [PATCH 01/62] [framework] fix mongo.exe dispose --- .../Volo/Abp/MongoDB/AbpMongoDbTestModule.cs | 7 ++---- .../Abp/MongoDB/Auditing/Auditing_Tests.cs | 2 ++ .../MultiTenant_Creation_Tests.cs | 2 ++ .../DataFiltering/MultiTenant_Filter_Tests.cs | 2 ++ .../DataFiltering/SoftDelete_Filter_Tests.cs | 2 ++ .../MongoDB/DataFiltering/SoftDelete_Tests.cs | 2 ++ .../MongoDB/Domain/ConcurrencyStamp_Tests.cs | 2 ++ .../MongoDB/Domain/ExtraProperties_Tests.cs | 2 ++ .../DomainEvents/DomainEvents_Tests.cs | 2 ++ .../DomainEvents/EntityChangeEvents_Tests.cs | 2 ++ .../Volo/Abp/MongoDB/MongoDbFixture.cs | 25 +++++++++++++++++++ .../Volo/Abp/MongoDB/MongoTestCollection.cs | 10 ++++++++ .../Repositories/Repository_Basic_Tests.cs | 1 + .../Repository_Basic_Tests_With_Int_Pk.cs | 1 + .../Repository_Queryable_Tests.cs | 2 ++ .../Repository_Specifications_Tests.cs | 2 ++ .../Abp/MongoDB/Uow/Uow_Completed_Tests.cs | 3 ++- 17 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs create mode 100644 framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoTestCollection.cs diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/AbpMongoDbTestModule.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/AbpMongoDbTestModule.cs index 86620366b3..112ecaf34d 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/AbpMongoDbTestModule.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/AbpMongoDbTestModule.cs @@ -1,6 +1,5 @@ using System; using Microsoft.Extensions.DependencyInjection; -using Mongo2Go; using Volo.Abp.Data; using Volo.Abp.Modularity; using Volo.Abp.TestApp; @@ -15,13 +14,11 @@ namespace Volo.Abp.MongoDB )] public class AbpMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.GetConnectionString().EnsureEndsWith('/') + "Db_" + - Guid.NewGuid().ToString("N"); + Guid.NewGuid().ToString("N"); Configure(options => { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Auditing/Auditing_Tests.cs index c5d7b87752..e20cceaf0e 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Auditing/Auditing_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Auditing/Auditing_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.Auditing { + [Collection("MongoDB Collection")] public class Auditing_Tests : Auditing_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Creation_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Creation_Tests.cs index a1ee94a943..3e4e532f5a 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Creation_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Creation_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.DataFiltering { + [Collection("MongoDB Collection")] public class MultiTenant_Creation_Tests : MultiTenant_Creation_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Filter_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Filter_Tests.cs index c23761a05c..c4ace6f1fe 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Filter_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Filter_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.DataFiltering { + [Collection("MongoDB Collection")] public class MultiTenant_Filter_Tests : MultiTenant_Filter_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Filter_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Filter_Tests.cs index 3636b998bb..6a6167133b 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Filter_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Filter_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.DataFiltering { + [Collection("MongoDB Collection")] public class SoftDelete_Filter_Tests : SoftDelete_Filter_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Tests.cs index fdd8ec1a5e..f3d9d75c9e 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.DataFiltering { + [Collection("MongoDB Collection")] public class SoftDelete_Tests : SoftDelete_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ConcurrencyStamp_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ConcurrencyStamp_Tests.cs index 117cd5dde1..4882e43c13 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ConcurrencyStamp_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ConcurrencyStamp_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.Domain { + [Collection("MongoDB Collection")] public class ConcurrencyStamp_Tests : ConcurrencyStamp_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ExtraProperties_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ExtraProperties_Tests.cs index b389ad61b9..9a4675e24f 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ExtraProperties_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ExtraProperties_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.Domain { + [Collection("MongoDB Collection")] public class ExtraProperties_Tests : ExtraProperties_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/DomainEvents_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/DomainEvents_Tests.cs index b01cb690f6..a9f232b989 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/DomainEvents_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/DomainEvents_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.DomainEvents { + [Collection("MongoDB Collection")] public class DomainEvents_Tests : DomainEvents_Tests { } diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/EntityChangeEvents_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/EntityChangeEvents_Tests.cs index 0df187f34e..3f4882b524 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/EntityChangeEvents_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/EntityChangeEvents_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.DomainEvents { + [Collection("MongoDB Collection")] public class EntityChangeEvents_Tests : EntityChangeEvents_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..0e483febab --- /dev/null +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs @@ -0,0 +1,25 @@ +using System; +using Mongo2Go; + +namespace Volo.Abp.MongoDB +{ + public class MongoDbFixture : IDisposable + { + public static MongoDbRunner MongoDbRunner; + + public MongoDbFixture() + { + MongoDbRunner = MongoDbRunner.Start(); + } + + public static string GetConnectionString() + { + return MongoDbRunner.ConnectionString; + } + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoTestCollection.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..2d57ed24fa --- /dev/null +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.MongoDB +{ + [CollectionDefinition("MongoDB Collection")] + public class MongoTestCollection : ICollectionFixture + { + + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs index 72774be545..112044d22d 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs @@ -9,6 +9,7 @@ using Xunit; namespace Volo.Abp.MongoDB.Repositories { + [Collection("MongoDB Collection")] public class Repository_Basic_Tests : Repository_Basic_Tests { [Fact] diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests_With_Int_Pk.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests_With_Int_Pk.cs index 58914e8e50..a3ae87cf58 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests_With_Int_Pk.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests_With_Int_Pk.cs @@ -3,6 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.Repositories { + [Collection("MongoDB Collection")] public class Repository_Basic_Tests_With_Int_Pk : Repository_Basic_Tests_With_Int_Pk { [Fact(Skip = "Int PKs are not working for MongoDb")] diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Queryable_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Queryable_Tests.cs index d396471bed..f75a5b4a34 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Queryable_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Queryable_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.Repositories { + [Collection("MongoDB Collection")] public class Repository_Queryable_Tests : Repository_Queryable_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs index 6529b4c5a9..c0a4439f09 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs @@ -1,7 +1,9 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.Repositories { + [Collection("MongoDB Collection")] public class Repository_Specifications_Tests : Repository_Specifications_Tests { } diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Uow/Uow_Completed_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Uow/Uow_Completed_Tests.cs index 4773644885..00bae8dc12 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Uow/Uow_Completed_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Uow/Uow_Completed_Tests.cs @@ -1,9 +1,10 @@ using Volo.Abp.TestApp.Testing; +using Xunit; namespace Volo.Abp.MongoDB.Uow { + [Collection("MongoDB Collection")] public class Uow_Completed_Tests : Uow_Completed_Tests { - } } \ No newline at end of file From ca57cf3ea70fa551026d8de09bb115da26e63d3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 02:39:10 +0300 Subject: [PATCH 02/62] refactor --- .../Volo/Abp/MongoDB/AbpMongoDbTestModule.cs | 2 +- .../Volo/Abp/MongoDB/Auditing/Auditing_Tests.cs | 2 +- .../DataFiltering/MultiTenant_Creation_Tests.cs | 2 +- .../DataFiltering/MultiTenant_Filter_Tests.cs | 2 +- .../DataFiltering/SoftDelete_Filter_Tests.cs | 2 +- .../Abp/MongoDB/DataFiltering/SoftDelete_Tests.cs | 2 +- .../Abp/MongoDB/Domain/ConcurrencyStamp_Tests.cs | 2 +- .../Abp/MongoDB/Domain/ExtraProperties_Tests.cs | 2 +- .../Abp/MongoDB/DomainEvents/DomainEvents_Tests.cs | 2 +- .../DomainEvents/EntityChangeEvents_Tests.cs | 2 +- .../Volo/Abp/MongoDB/MongoDbFixture.cs | 13 ++----------- .../Volo/Abp/MongoDB/MongoTestCollection.cs | 4 ++-- .../MongoDB/Repositories/Repository_Basic_Tests.cs | 2 +- .../Repository_Basic_Tests_With_Int_Pk.cs | 2 +- .../Repositories/Repository_Queryable_Tests.cs | 2 +- .../Repositories/Repository_Specifications_Tests.cs | 2 +- .../Volo/Abp/MongoDB/Uow/Uow_Completed_Tests.cs | 2 +- .../Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs | 7 +++++++ .../Abp/AuditLogging/MongoDB/MongoTestCollection.cs | 7 +++++++ 19 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs create mode 100644 modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoTestCollection.cs diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/AbpMongoDbTestModule.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/AbpMongoDbTestModule.cs index 112ecaf34d..cfa7b033b7 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/AbpMongoDbTestModule.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/AbpMongoDbTestModule.cs @@ -16,7 +16,7 @@ namespace Volo.Abp.MongoDB { public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbFixture.GetConnectionString().EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Auditing/Auditing_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Auditing/Auditing_Tests.cs index e20cceaf0e..b08c2120a4 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Auditing/Auditing_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Auditing/Auditing_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.Auditing { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class Auditing_Tests : Auditing_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Creation_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Creation_Tests.cs index 3e4e532f5a..0ab365a568 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Creation_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Creation_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.DataFiltering { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class MultiTenant_Creation_Tests : MultiTenant_Creation_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Filter_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Filter_Tests.cs index c4ace6f1fe..c32b640e03 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Filter_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/MultiTenant_Filter_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.DataFiltering { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class MultiTenant_Filter_Tests : MultiTenant_Filter_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Filter_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Filter_Tests.cs index 6a6167133b..5de6caf661 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Filter_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Filter_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.DataFiltering { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class SoftDelete_Filter_Tests : SoftDelete_Filter_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Tests.cs index f3d9d75c9e..bffb38f858 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DataFiltering/SoftDelete_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.DataFiltering { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class SoftDelete_Tests : SoftDelete_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ConcurrencyStamp_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ConcurrencyStamp_Tests.cs index 4882e43c13..88bf889b7c 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ConcurrencyStamp_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ConcurrencyStamp_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.Domain { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class ConcurrencyStamp_Tests : ConcurrencyStamp_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ExtraProperties_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ExtraProperties_Tests.cs index 9a4675e24f..5f8f8784c3 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ExtraProperties_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Domain/ExtraProperties_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.Domain { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class ExtraProperties_Tests : ExtraProperties_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/DomainEvents_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/DomainEvents_Tests.cs index a9f232b989..ed480a229e 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/DomainEvents_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/DomainEvents_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.DomainEvents { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class DomainEvents_Tests : DomainEvents_Tests { } diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/EntityChangeEvents_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/EntityChangeEvents_Tests.cs index 3f4882b524..184142d77c 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/EntityChangeEvents_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/DomainEvents/EntityChangeEvents_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.DomainEvents { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class EntityChangeEvents_Tests : EntityChangeEvents_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs index 0e483febab..f401d0d6d4 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDbFixture.cs @@ -5,18 +5,9 @@ namespace Volo.Abp.MongoDB { public class MongoDbFixture : IDisposable { - public static MongoDbRunner MongoDbRunner; + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; - public MongoDbFixture() - { - MongoDbRunner = MongoDbRunner.Start(); - } - - public static string GetConnectionString() - { - return MongoDbRunner.ConnectionString; - } - public void Dispose() { MongoDbRunner?.Dispose(); diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoTestCollection.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoTestCollection.cs index 2d57ed24fa..ae23dc9450 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoTestCollection.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoTestCollection.cs @@ -2,9 +2,9 @@ using Xunit; namespace Volo.Abp.MongoDB { - [CollectionDefinition("MongoDB Collection")] + [CollectionDefinition(Name)] public class MongoTestCollection : ICollectionFixture { - + public const string Name = "MongoDB Collection"; } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs index 112044d22d..64af3aa855 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests.cs @@ -9,7 +9,7 @@ using Xunit; namespace Volo.Abp.MongoDB.Repositories { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class Repository_Basic_Tests : Repository_Basic_Tests { [Fact] diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests_With_Int_Pk.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests_With_Int_Pk.cs index a3ae87cf58..2b8be02617 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests_With_Int_Pk.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Basic_Tests_With_Int_Pk.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.Repositories { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class Repository_Basic_Tests_With_Int_Pk : Repository_Basic_Tests_With_Int_Pk { [Fact(Skip = "Int PKs are not working for MongoDb")] diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Queryable_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Queryable_Tests.cs index f75a5b4a34..05ffb6ae27 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Queryable_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Queryable_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.Repositories { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class Repository_Queryable_Tests : Repository_Queryable_Tests { diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs index c0a4439f09..4670ac5d00 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/Repository_Specifications_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.Repositories { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class Repository_Specifications_Tests : Repository_Specifications_Tests { } diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Uow/Uow_Completed_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Uow/Uow_Completed_Tests.cs index 00bae8dc12..cef724b9bd 100644 --- a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Uow/Uow_Completed_Tests.cs +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Uow/Uow_Completed_Tests.cs @@ -3,7 +3,7 @@ using Xunit; namespace Volo.Abp.MongoDB.Uow { - [Collection("MongoDB Collection")] + [Collection(MongoTestCollection.Name)] public class Uow_Completed_Tests : Uow_Completed_Tests { } diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..cc2f90e01d --- /dev/null +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.AuditLogging.MongoDB +{ + public class MongoDbFixture + { + + } +} \ No newline at end of file diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoTestCollection.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..4ed185cb9c --- /dev/null +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoTestCollection.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.AuditLogging.MongoDB +{ + public class MongoTestCollection + { + + } +} \ No newline at end of file From 1645483bd0b2cce12eb53934c2728184d49e2f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 02:40:06 +0300 Subject: [PATCH 03/62] [modules/auditlogging] fix mongo.exe dispose --- .../MongoDB/AbpAuditLoggingMongoDbTestModule.cs | 4 +--- .../AuditLogging/MongoDB/AuditLogRepository_Tests.cs | 2 ++ .../AuditLogging/MongoDB/AuditStore_Basic_Tests.cs | 5 ++++- .../Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs | 11 ++++++++++- .../Abp/AuditLogging/MongoDB/MongoTestCollection.cs | 7 +++++-- 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AbpAuditLoggingMongoDbTestModule.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AbpAuditLoggingMongoDbTestModule.cs index 7c70d720ec..b57c204818 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AbpAuditLoggingMongoDbTestModule.cs +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AbpAuditLoggingMongoDbTestModule.cs @@ -12,11 +12,9 @@ namespace Volo.Abp.AuditLogging.MongoDB )] public class AbpAuditLoggingMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditLogRepository_Tests.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditLogRepository_Tests.cs index ffcc2094ab..a0edd58a45 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditLogRepository_Tests.cs +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditLogRepository_Tests.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Text; +using Xunit; namespace Volo.Abp.AuditLogging.MongoDB { + [Collection(MongoTestCollection.Name)] public class AuditLogRepository_Tests : AuditLogRepository_Tests { diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditStore_Basic_Tests.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditStore_Basic_Tests.cs index fa7a41ecfd..ad590c61e1 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditStore_Basic_Tests.cs +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AuditStore_Basic_Tests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.AuditLogging.MongoDB +using Xunit; + +namespace Volo.Abp.AuditLogging.MongoDB { + [Collection(MongoTestCollection.Name)] public class AuditStore_Basic_Tests : AuditStore_Basic_Tests { diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs index cc2f90e01d..33803c1af4 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoDbFixture.cs @@ -1,7 +1,16 @@ +using System; +using Mongo2Go; + namespace Volo.Abp.AuditLogging.MongoDB { - public class MongoDbFixture + public class MongoDbFixture : IDisposable { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + public void Dispose() + { + MongoDbRunner?.Dispose(); + } } } \ No newline at end of file diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoTestCollection.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoTestCollection.cs index 4ed185cb9c..1d89caa8b0 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoTestCollection.cs +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/MongoTestCollection.cs @@ -1,7 +1,10 @@ +using Xunit; + namespace Volo.Abp.AuditLogging.MongoDB { - public class MongoTestCollection + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture { - + public const string Name = "MongoDB Collection"; } } \ No newline at end of file From 734059d9ff727aec681fe5693be6beddbab1ef9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 02:46:26 +0300 Subject: [PATCH 04/62] [modules/backgroundjobs] fix mongo.exe dispose --- .../AbpBackgroundJobsMongoDbTestModule.cs | 6 +----- .../MongoDB/BackgroundJobRepositoryTests.cs | 5 ++++- .../Abp/BackgroundJobs/MongoDB/MongoDbFixture.cs | 16 ++++++++++++++++ .../MongoDB/MongoTestCollection.cs | 10 ++++++++++ 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoDbFixture.cs create mode 100644 modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoTestCollection.cs diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/AbpBackgroundJobsMongoDbTestModule.cs b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/AbpBackgroundJobsMongoDbTestModule.cs index 4147917829..07be76b792 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/AbpBackgroundJobsMongoDbTestModule.cs +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/AbpBackgroundJobsMongoDbTestModule.cs @@ -1,6 +1,4 @@ using System; -using Microsoft.Extensions.DependencyInjection; -using Mongo2Go; using Volo.Abp.Data; using Volo.Abp.Modularity; @@ -12,11 +10,9 @@ namespace Volo.Abp.BackgroundJobs.MongoDB )] public class AbpBackgroundJobsMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/BackgroundJobRepositoryTests.cs b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/BackgroundJobRepositoryTests.cs index 429cf4821f..f3827c50e2 100644 --- a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/BackgroundJobRepositoryTests.cs +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/BackgroundJobRepositoryTests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.BackgroundJobs.MongoDB +using Xunit; + +namespace Volo.Abp.BackgroundJobs.MongoDB { + [Collection((MongoTestCollection.Name))] public class BackgroundJobRepositoryTests : BackgroundJobRepository_Tests { diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoDbFixture.cs b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..3ae1d8fc10 --- /dev/null +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace Volo.Abp.BackgroundJobs.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoTestCollection.cs b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..0096ae6f10 --- /dev/null +++ b/modules/background-jobs/test/Volo.Abp.BackgroundJobs.MongoDB.Tests/Volo/Abp/BackgroundJobs/MongoDB/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.BackgroundJobs.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file From db4a558f064113306872643fa967fd73410b987d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 02:48:07 +0300 Subject: [PATCH 05/62] remove unused usings --- .../AuditLogging/MongoDB/AbpAuditLoggingMongoDbTestModule.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AbpAuditLoggingMongoDbTestModule.cs b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AbpAuditLoggingMongoDbTestModule.cs index b57c204818..5cac32faae 100644 --- a/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AbpAuditLoggingMongoDbTestModule.cs +++ b/modules/audit-logging/test/Volo.Abp.AuditLogging.MongoDB.Tests/Volo/Abp/AuditLogging/MongoDB/AbpAuditLoggingMongoDbTestModule.cs @@ -1,6 +1,4 @@ using System; -using Microsoft.Extensions.DependencyInjection; -using Mongo2Go; using Volo.Abp.Data; using Volo.Abp.Modularity; From 4adaf68123aa4d86db728b4755575db64a7e22dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 02:55:26 +0300 Subject: [PATCH 06/62] [modules/blogging] fix mongo.exe dispose --- .../Volo/Blogging/Blogs/BlogRepository_Tests.cs | 2 ++ .../Blogging/Comments/CommentRepository_Tests.cs | 2 ++ .../MongoDB/BloggingMongoDBTestModule.cs | 6 +----- .../Volo/Blogging/MongoDB/MongoDbFixture.cs | 16 ++++++++++++++++ .../Volo/Blogging/MongoDB/MongoTestCollection.cs | 10 ++++++++++ .../Volo/Blogging/Posts/PostRepository_Tests.cs | 2 ++ .../Volo/Blogging/Tagging/TagRepository_Tests.cs | 2 ++ 7 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoDbFixture.cs create mode 100644 modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoTestCollection.cs diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs index 76044f257c..060d3b86fc 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Blogs/BlogRepository_Tests.cs @@ -1,8 +1,10 @@ using Volo.Blogging.Blogs; using Volo.Blogging.MongoDB; +using Xunit; namespace Volo.Blogging { + [Collection(MongoTestCollection.Name)] public class BlogRepository_Tests : BlogRepository_Tests { } diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs index a001f9b0d9..d956cf1b15 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Comments/CommentRepository_Tests.cs @@ -1,8 +1,10 @@ using Volo.Blogging.Comments; using Volo.Blogging.MongoDB; +using Xunit; namespace Volo.Blogging { + [Collection(MongoTestCollection.Name)] public class CommentRepository_Tests : CommentRepository_Tests { } diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs index 4c4b34ef54..1e68cebf21 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/BloggingMongoDBTestModule.cs @@ -1,6 +1,4 @@ using System; -using Mongo2Go; -using Volo.Abp; using Volo.Abp.Data; using Volo.Abp.Modularity; @@ -12,11 +10,9 @@ namespace Volo.Blogging.MongoDB )] public class BloggingMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoDbFixture.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..d5fbfb4451 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace Volo.Blogging.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoTestCollection.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..f075e5dd07 --- /dev/null +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/MongoDB/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Blogging.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs index d0e13ba403..814f0d206e 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Posts/PostRepository_Tests.cs @@ -1,8 +1,10 @@ using Volo.Blogging.Posts; using Volo.Blogging.MongoDB; +using Xunit; namespace Volo.Blogging { + [Collection(MongoTestCollection.Name)] public class PostRepository_Tests : PostRepository_Tests { } diff --git a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs index 8f13c6c6b0..66cb0d52c5 100644 --- a/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs +++ b/modules/blogging/test/Volo.Blogging.MongoDB.Tests/Volo/Blogging/Tagging/TagRepository_Tests.cs @@ -1,8 +1,10 @@ using Volo.Blogging.Tagging; using Volo.Blogging.MongoDB; +using Xunit; namespace Volo.Blogging { + [Collection(MongoTestCollection.Name)] public class TagRepository_Tests : TagRepository_Tests { } From 8bf5a24fbc40e01ebc8fd56bc7d17557cfd1168e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 03:14:34 +0300 Subject: [PATCH 07/62] [modules/docs] fix mongo.exe dispose --- .../Volo/Docs/MongoDB/DocsMongoDBTestModule.cs | 6 +----- .../Volo/Docs/MongoDB/MongoDbFixture.cs | 16 ++++++++++++++++ .../Volo/Docs/MongoDB/MongoTestCollection.cs | 10 ++++++++++ .../Volo/Docs/Project/ProjectRepository_Tests.cs | 2 ++ 4 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoDbFixture.cs create mode 100644 modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoTestCollection.cs diff --git a/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/DocsMongoDBTestModule.cs b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/DocsMongoDBTestModule.cs index c5fef83ddb..b31f9b5acd 100644 --- a/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/DocsMongoDBTestModule.cs +++ b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/DocsMongoDBTestModule.cs @@ -1,6 +1,4 @@ using System; -using Mongo2Go; -using Volo.Abp; using Volo.Abp.Data; using Volo.Abp.Modularity; @@ -12,11 +10,9 @@ namespace Volo.Docs.MongoDB )] public class DocsMongoDBTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoDbFixture.cs b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..fc920813ab --- /dev/null +++ b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace Volo.Docs.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoTestCollection.cs b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..86505c5a73 --- /dev/null +++ b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/MongoDB/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Docs.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file diff --git a/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/Project/ProjectRepository_Tests.cs b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/Project/ProjectRepository_Tests.cs index 6f2df01a11..696dd31185 100644 --- a/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/Project/ProjectRepository_Tests.cs +++ b/modules/docs/test/Volo.Docs.MongoDB.Tests/Volo/Docs/Project/ProjectRepository_Tests.cs @@ -1,7 +1,9 @@ using Volo.Docs.MongoDB; +using Xunit; namespace Volo.Docs.Project { + [Collection(MongoTestCollection.Name)] public class ProjectRepository_Tests : ProjectRepository_Tests { } From 993d5c5a09320c92a9c4fa8bbf7bd0c22e385943 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 03:24:53 +0300 Subject: [PATCH 08/62] [modules/featuremanagement] fix mongo.exe dispose --- .../AbpFeatureManagementMongoDbTestModule.cs | 5 +---- .../MongoDB/FeatureManagementStore_Tests.cs | 5 ++--- .../MongoDB/FeatureValueRepositoryTests.cs | 5 ++++- .../FeatureManagement/MongoDB/MongoDbFixture.cs | 16 ++++++++++++++++ .../MongoDB/MongoTestCollection.cs | 10 ++++++++++ 5 files changed, 33 insertions(+), 8 deletions(-) create mode 100644 modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoDbFixture.cs create mode 100644 modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoTestCollection.cs diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs index 00b5b22871..04d4ac916f 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/AbpFeatureManagementMongoDbTestModule.cs @@ -1,5 +1,4 @@ using System; -using Mongo2Go; using Volo.Abp.Data; using Volo.Abp.Modularity; @@ -11,11 +10,9 @@ namespace Volo.Abp.FeatureManagement.MongoDB )] public class AbpFeatureManagementMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementStore_Tests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementStore_Tests.cs index 7df6e3516b..0337bc8822 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementStore_Tests.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureManagementStore_Tests.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; +using Xunit; namespace Volo.Abp.FeatureManagement.MongoDB { + [Collection(MongoTestCollection.Name)] public class FeatureManagementStore_Tests : FeatureManagementStore_Tests { diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureValueRepositoryTests.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureValueRepositoryTests.cs index 6184e4c90f..9654d8ab27 100644 --- a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureValueRepositoryTests.cs +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/FeatureValueRepositoryTests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.FeatureManagement.MongoDB +using Xunit; + +namespace Volo.Abp.FeatureManagement.MongoDB { + [Collection(MongoTestCollection.Name)] public class FeatureValueRepositoryTests : FeatureValueRepository_Tests { diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoDbFixture.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..bfd7fa9c04 --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace Volo.Abp.FeatureManagement.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoTestCollection.cs b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..c8a6e1c58c --- /dev/null +++ b/modules/feature-management/test/Volo.Abp.FeatureManagement.MongoDB.Tests/Volo/Abp/FeatureManagement/MongoDB/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.FeatureManagement.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file From 96f7b57ab73611c1eec297329d1042808a21d7a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 03:28:49 +0300 Subject: [PATCH 09/62] [modules/identity] fix mongo.exe dispose --- .../MongoDB/AbpIdentityMongoDbTestModule.cs | 6 +----- .../MongoDB/IdentityClaimTypeRepository_Tests.cs | 5 ++++- .../Identity/MongoDB/IdentityDataSeeder_Tests.cs | 5 ++++- .../MongoDB/IdentityRoleRepository_Tests.cs | 5 ++++- .../MongoDB/IdentityUserRepository_Tests.cs | 5 ++++- .../MongoDB/Identity_Repository_Resolve_Tests.cs | 5 ++++- .../Volo/Abp/Identity/MongoDB/MongoDbFixture.cs | 16 ++++++++++++++++ .../Abp/Identity/MongoDB/MongoTestCollection.cs | 10 ++++++++++ 8 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoDbFixture.cs create mode 100644 modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoTestCollection.cs diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbTestModule.cs b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbTestModule.cs index 6e9f3806a2..f730e5f445 100644 --- a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbTestModule.cs +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/AbpIdentityMongoDbTestModule.cs @@ -1,6 +1,4 @@ using System; -using Microsoft.Extensions.DependencyInjection; -using Mongo2Go; using Volo.Abp.Data; using Volo.Abp.Modularity; using Volo.Abp.PermissionManagement.MongoDB; @@ -14,11 +12,9 @@ namespace Volo.Abp.Identity.MongoDB )] public class AbpIdentityMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityClaimTypeRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityClaimTypeRepository_Tests.cs index b7926aae00..cc66856c06 100644 --- a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityClaimTypeRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityClaimTypeRepository_Tests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.Identity.MongoDB +using Xunit; + +namespace Volo.Abp.Identity.MongoDB { + [Collection(MongoTestCollection.Name)] public class IdentityClaimTypeRepository_Tests : IdentityClaimTypeRepository_Tests { diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityDataSeeder_Tests.cs b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityDataSeeder_Tests.cs index e97269c7e2..d614807395 100644 --- a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityDataSeeder_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityDataSeeder_Tests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.Identity.MongoDB +using Xunit; + +namespace Volo.Abp.Identity.MongoDB { + [Collection(MongoTestCollection.Name)] public class IdentityDataSeeder_Tests : IdentityDataSeeder_Tests { diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityRoleRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityRoleRepository_Tests.cs index 4873aa7298..8fde9dbb2c 100644 --- a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityRoleRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityRoleRepository_Tests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.Identity.MongoDB +using Xunit; + +namespace Volo.Abp.Identity.MongoDB { + [Collection(MongoTestCollection.Name)] public class IdentityRoleRepository_Tests : IdentityRoleRepository_Tests { diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityUserRepository_Tests.cs b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityUserRepository_Tests.cs index 214d8f6986..e29a216ca7 100644 --- a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityUserRepository_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/IdentityUserRepository_Tests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.Identity.MongoDB +using Xunit; + +namespace Volo.Abp.Identity.MongoDB { + [Collection(MongoTestCollection.Name)] public class IdentityUserRepository_Tests : IdentityUserRepository_Tests { diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/Identity_Repository_Resolve_Tests.cs b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/Identity_Repository_Resolve_Tests.cs index 5d6742adef..42020a4170 100644 --- a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/Identity_Repository_Resolve_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/Identity_Repository_Resolve_Tests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.Identity.MongoDB +using Xunit; + +namespace Volo.Abp.Identity.MongoDB { + [Collection(MongoTestCollection.Name)] public class Identity_Repository_Resolve_Tests : Identity_Repository_Resolve_Tests { } diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoDbFixture.cs b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..26de0a9399 --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace Volo.Abp.Identity.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoTestCollection.cs b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..ae01262908 --- /dev/null +++ b/modules/identity/test/Volo.Abp.Identity.MongoDB.Tests/Volo/Abp/Identity/MongoDB/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.Identity.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file From 18344cbdae4780a372b083dbc4f3129aaef15eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 03:37:57 +0300 Subject: [PATCH 10/62] [modules/identityserver] fix mongo.exe dispose --- .../AbpIdentityServerMongoDbTestModule.cs | 5 +---- .../ApiResourceRepository_Tests.cs | 4 +++- .../Abp/IdentityServer/ClientRepository_Tests.cs | 5 ++--- .../IdentityResourceRepository_Tests.cs | 5 ++--- .../Volo/Abp/IdentityServer/MongoDbFixture.cs | 16 ++++++++++++++++ .../Abp/IdentityServer/MongoTestCollection.cs | 10 ++++++++++ .../PersistentGrantRepository_Tests.cs | 5 ++--- 7 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoDbFixture.cs create mode 100644 modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoTestCollection.cs diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/AbpIdentityServerMongoDbTestModule.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/AbpIdentityServerMongoDbTestModule.cs index ebfa34404d..d1721c720f 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/AbpIdentityServerMongoDbTestModule.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/AbpIdentityServerMongoDbTestModule.cs @@ -1,5 +1,4 @@ using System; -using Mongo2Go; using Volo.Abp.Data; using Volo.Abp.Identity.MongoDB; using Volo.Abp.IdentityServer.MongoDB; @@ -15,11 +14,9 @@ namespace Volo.Abp.IdentityServer )] public class AbpIdentityServerMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/ApiResourceRepository_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/ApiResourceRepository_Tests.cs index 032505266d..dfe00ab4dd 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/ApiResourceRepository_Tests.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/ApiResourceRepository_Tests.cs @@ -1,6 +1,8 @@ - +using Xunit; + namespace Volo.Abp.IdentityServer { + [Collection(MongoTestCollection.Name)] public class ApiResourceRepository_Tests : ApiResourceRepository_Tests { } diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/ClientRepository_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/ClientRepository_Tests.cs index 29b006cd99..766448daeb 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/ClientRepository_Tests.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/ClientRepository_Tests.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; +using Xunit; namespace Volo.Abp.IdentityServer { + [Collection(MongoTestCollection.Name)] public class ClientRepository_Tests : ClientRepository_Tests { diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/IdentityResourceRepository_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/IdentityResourceRepository_Tests.cs index 2b44b2c6a0..36ca210b36 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/IdentityResourceRepository_Tests.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/IdentityResourceRepository_Tests.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; +using Xunit; namespace Volo.Abp.IdentityServer { + [Collection(MongoTestCollection.Name)] public class IdentityResourceRepository_Tests : IdentityResourceRepository_Tests { } diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoDbFixture.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoDbFixture.cs new file mode 100644 index 0000000000..22bbf08ef4 --- /dev/null +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace Volo.Abp.IdentityServer +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoTestCollection.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoTestCollection.cs new file mode 100644 index 0000000000..8ac0a73624 --- /dev/null +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.IdentityServer +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file diff --git a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/PersistentGrantRepository_Tests.cs b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/PersistentGrantRepository_Tests.cs index fd2763dc6c..834d18fcf9 100644 --- a/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/PersistentGrantRepository_Tests.cs +++ b/modules/identityserver/test/Volo.Abp.IdentityServer.MongoDB.Tests/Volo/Abp/IdentityServer/PersistentGrantRepository_Tests.cs @@ -1,9 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Text; +using Xunit; namespace Volo.Abp.IdentityServer { + [Collection(MongoTestCollection.Name)] public class PersistentGrantRepository_Tests : PersistentGrantRepository_Tests { From cf995af8bf534a1ee329c620d5623c56a2406f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 03:38:33 +0300 Subject: [PATCH 11/62] [modules/permissionmanagement] fix mongo.exe dispose --- .../AbpPermissionManagementMongoDbTestModule.cs | 5 +---- .../MongoDb/MongoDbFixture.cs | 16 ++++++++++++++++ .../MongoDb/MongoTestCollection.cs | 10 ++++++++++ .../MongoDb/PermissionGrantRepository_Tests.cs | 5 ++++- 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbFixture.cs create mode 100644 modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoTestCollection.cs diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbTestModule.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbTestModule.cs index d3d4d5d6e9..8e3c7b2c22 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbTestModule.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/AbpPermissionManagementMongoDbTestModule.cs @@ -1,5 +1,4 @@ using System; -using Mongo2Go; using Volo.Abp.Data; using Volo.Abp.Modularity; @@ -10,11 +9,9 @@ namespace Volo.Abp.PermissionManagement.MongoDB typeof(AbpPermissionManagementTestBaseModule))] public class AbpPermissionManagementMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbFixture.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbFixture.cs new file mode 100644 index 0000000000..3a09e6e1a4 --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace Volo.Abp.PermissionManagement.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoTestCollection.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoTestCollection.cs new file mode 100644 index 0000000000..a1f2eb6558 --- /dev/null +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.PermissionManagement.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file diff --git a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/PermissionGrantRepository_Tests.cs b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/PermissionGrantRepository_Tests.cs index 98db0640c5..dd3c42da15 100644 --- a/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/PermissionGrantRepository_Tests.cs +++ b/modules/permission-management/test/Volo.Abp.PermissionManagement.MongoDB.Tests/Volo/Abp/PermissionManagement/MongoDb/PermissionGrantRepository_Tests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.PermissionManagement.MongoDB +using Xunit; + +namespace Volo.Abp.PermissionManagement.MongoDB { + [Collection(MongoTestCollection.Name)] public class PermissionGrantRepository_Tests : PermissionGrantRepository_Tests { From be9b73b83528a3e19964bae8940b2226d8092280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 03:42:34 +0300 Subject: [PATCH 12/62] [modules/settingmanagement] fix mongo.exe dispose --- .../AbpSettingManagementMongoDbTestModule.cs | 5 +---- .../SettingManagement/MongoDB/MongoDbFixture.cs | 16 ++++++++++++++++ .../MongoDB/MongoTestCollection.cs | 10 ++++++++++ .../MongoDB/SettingRepository_Tests.cs | 5 ++++- 4 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoDbFixture.cs create mode 100644 modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoTestCollection.cs diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/AbpSettingManagementMongoDbTestModule.cs b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/AbpSettingManagementMongoDbTestModule.cs index 1b9faefdbd..ec221eba15 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/AbpSettingManagementMongoDbTestModule.cs +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/AbpSettingManagementMongoDbTestModule.cs @@ -1,5 +1,4 @@ using System; -using Mongo2Go; using Volo.Abp.Data; using Volo.Abp.Modularity; @@ -11,11 +10,9 @@ namespace Volo.Abp.SettingManagement.MongoDB )] public class AbpSettingManagementMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoDbFixture.cs b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..31ccdcc4f7 --- /dev/null +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace Volo.Abp.SettingManagement.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoTestCollection.cs b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..11ed1cbb55 --- /dev/null +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.SettingManagement.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file diff --git a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/SettingRepository_Tests.cs b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/SettingRepository_Tests.cs index 7e35eff799..a1a2d1428c 100644 --- a/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/SettingRepository_Tests.cs +++ b/modules/setting-management/test/Volo.Abp.SettingManagement.MongoDB.Tests/Volo/Abp/SettingManagement/MongoDB/SettingRepository_Tests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.SettingManagement.MongoDB +using Xunit; + +namespace Volo.Abp.SettingManagement.MongoDB { + [Collection(MongoTestCollection.Name)] public class SettingRepository_Tests : SettingRepository_Tests { From 7fa2dbc886bf93b0e303f9257fd1766924749eb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 03:45:39 +0300 Subject: [PATCH 13/62] [modules/tenantmanagement] fix mongo.exe dispose --- .../AbpTenantManagementMongoDbTestModule.cs | 6 +----- .../TenantManagement/MongoDb/MongoDbFixture.cs | 16 ++++++++++++++++ .../MongoDb/MongoTestCollection.cs | 10 ++++++++++ .../MongoDb/TenantRepository_Tests.cs | 5 ++++- 4 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoDbFixture.cs create mode 100644 modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoTestCollection.cs diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbTestModule.cs b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbTestModule.cs index 911839d510..9584ec2d44 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbTestModule.cs +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/AbpTenantManagementMongoDbTestModule.cs @@ -1,6 +1,4 @@ using System; -using Microsoft.Extensions.DependencyInjection; -using Mongo2Go; using Volo.Abp.Data; using Volo.Abp.Modularity; @@ -12,11 +10,9 @@ namespace Volo.Abp.TenantManagement.MongoDB )] public class AbpTenantManagementMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoDbFixture.cs b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoDbFixture.cs new file mode 100644 index 0000000000..be115d9dff --- /dev/null +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace Volo.Abp.TenantManagement.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoTestCollection.cs b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoTestCollection.cs new file mode 100644 index 0000000000..1719b7dbdd --- /dev/null +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace Volo.Abp.TenantManagement.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file diff --git a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/TenantRepository_Tests.cs b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/TenantRepository_Tests.cs index b1ae97b021..0a980be37d 100644 --- a/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/TenantRepository_Tests.cs +++ b/modules/tenant-management/test/Volo.Abp.TenantManagement.MongoDB.Tests/Volo/Abp/TenantManagement/MongoDb/TenantRepository_Tests.cs @@ -1,5 +1,8 @@ -namespace Volo.Abp.TenantManagement.MongoDB +using Xunit; + +namespace Volo.Abp.TenantManagement.MongoDB { + [Collection(MongoTestCollection.Name)] public class TenantRepository_Tests : TenantRepository_Tests { From 6f0db0b9b0a7d3734f72d1e79adffe710901d150 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 03:58:14 +0300 Subject: [PATCH 14/62] [templates/app] fix mongo.exe dispose --- .../MongoDb/MongoDbFixture.cs | 16 ++++++++++++++++ .../MongoDb/MongoTestCollection.cs | 10 ++++++++++ .../MongoDb/MyProjectNameMongoDbTestModule.cs | 5 +---- .../MongoDb/Samples/SampleRepositoryTests.cs | 1 + 4 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MongoDbFixture.cs create mode 100644 templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MongoTestCollection.cs diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MongoDbFixture.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MongoDbFixture.cs new file mode 100644 index 0000000000..0feac4eebf --- /dev/null +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace MyCompanyName.MyProjectName.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MongoTestCollection.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MongoTestCollection.cs new file mode 100644 index 0000000000..42c85760c3 --- /dev/null +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace MyCompanyName.MyProjectName.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MyProjectNameMongoDbTestModule.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MyProjectNameMongoDbTestModule.cs index 270ad2102c..bcaf7494a7 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MyProjectNameMongoDbTestModule.cs +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/MyProjectNameMongoDbTestModule.cs @@ -1,5 +1,4 @@ using System; -using Mongo2Go; using Volo.Abp.Data; using Volo.Abp.Modularity; @@ -11,11 +10,9 @@ namespace MyCompanyName.MyProjectName.MongoDB )] public class MyProjectNameMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/Samples/SampleRepositoryTests.cs b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/Samples/SampleRepositoryTests.cs index 27095ec117..17d8007c16 100644 --- a/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/Samples/SampleRepositoryTests.cs +++ b/templates/app/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDb/Samples/SampleRepositoryTests.cs @@ -13,6 +13,7 @@ namespace MyCompanyName.MyProjectName.MongoDB.Samples * (like default AppUser repository IRepository here). * Only test your custom repository methods. */ + [Collection(MongoTestCollection.Name)] public class SampleRepositoryTests : MyProjectNameMongoDbTestBase { private readonly IRepository _appUserRepository; From e44efcb803659f07001b648afe8c58334581e84f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mehmet=20Tu=CC=88ken?= Date: Wed, 18 Dec 2019 04:01:55 +0300 Subject: [PATCH 15/62] [templates/module] fix mongo.exe dispose --- .../MongoDB/MongoDbFixture.cs | 16 ++++++++++++++++ .../MongoDB/MongoTestCollection.cs | 10 ++++++++++ .../MongoDB/MyProjectNameMongoDbTestModule.cs | 6 +----- .../MongoDB/Samples/SampleRepository_Tests.cs | 2 ++ 4 files changed, 29 insertions(+), 5 deletions(-) create mode 100644 templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MongoDbFixture.cs create mode 100644 templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MongoTestCollection.cs diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MongoDbFixture.cs b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MongoDbFixture.cs new file mode 100644 index 0000000000..0feac4eebf --- /dev/null +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MongoDbFixture.cs @@ -0,0 +1,16 @@ +using System; +using Mongo2Go; + +namespace MyCompanyName.MyProjectName.MongoDB +{ + public class MongoDbFixture : IDisposable + { + private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); + public static readonly string ConnectionString = MongoDbRunner.ConnectionString; + + public void Dispose() + { + MongoDbRunner?.Dispose(); + } + } +} \ No newline at end of file diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MongoTestCollection.cs b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MongoTestCollection.cs new file mode 100644 index 0000000000..42c85760c3 --- /dev/null +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MongoTestCollection.cs @@ -0,0 +1,10 @@ +using Xunit; + +namespace MyCompanyName.MyProjectName.MongoDB +{ + [CollectionDefinition(Name)] + public class MongoTestCollection : ICollectionFixture + { + public const string Name = "MongoDB Collection"; + } +} \ No newline at end of file diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MyProjectNameMongoDbTestModule.cs b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MyProjectNameMongoDbTestModule.cs index 149db59bcc..f29c713c05 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MyProjectNameMongoDbTestModule.cs +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/MyProjectNameMongoDbTestModule.cs @@ -1,6 +1,4 @@ using System; -using Mongo2Go; -using Volo.Abp; using Volo.Abp.Data; using Volo.Abp.Modularity; @@ -12,11 +10,9 @@ namespace MyCompanyName.MyProjectName.MongoDB )] public class MyProjectNameMongoDbTestModule : AbpModule { - private static readonly MongoDbRunner MongoDbRunner = MongoDbRunner.Start(); - public override void ConfigureServices(ServiceConfigurationContext context) { - var connectionString = MongoDbRunner.ConnectionString.EnsureEndsWith('/') + + var connectionString = MongoDbFixture.ConnectionString.EnsureEndsWith('/') + "Db_" + Guid.NewGuid().ToString("N"); diff --git a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/Samples/SampleRepository_Tests.cs b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/Samples/SampleRepository_Tests.cs index 6522b9884e..c16c416b2c 100644 --- a/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/Samples/SampleRepository_Tests.cs +++ b/templates/module/aspnet-core/test/MyCompanyName.MyProjectName.MongoDB.Tests/MongoDB/Samples/SampleRepository_Tests.cs @@ -1,7 +1,9 @@ using MyCompanyName.MyProjectName.Samples; +using Xunit; namespace MyCompanyName.MyProjectName.MongoDB.Samples { + [Collection(MongoTestCollection.Name)] public class SampleRepository_Tests : SampleRepository_Tests { /* Don't write custom repository tests here, instead write to From c0d6582986007fb20a9fbce5fb3f1c97a1ee8592 Mon Sep 17 00:00:00 2001 From: Mehmet Perk Date: Thu, 16 Jan 2020 22:35:07 +0300 Subject: [PATCH 16/62] MethodInvocationAuthorizationService should check Roles --- .../MethodInvocationAuthorizationService.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs index 1e063f1f0e..82a0f9c2d1 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs @@ -64,20 +64,26 @@ namespace Volo.Abp.Authorization protected async Task CheckAsync(IAuthorizeData authorizationAttribute) { - if (authorizationAttribute.Policy == null) + if (authorizationAttribute.Policy != null) { - //TODO: Can we find a better, unified, way of checking if current request has been authenticated - if (!_currentUser.IsAuthenticated && !_currentClient.IsAuthenticated) + await _authorizationService.CheckAsync(authorizationAttribute.Policy).ConfigureAwait(false); + } + else if (authorizationAttribute.Roles != null) + { + if(_currentUser.IsInRole(authorizationAttribute.Roles) == false) { - throw new AbpAuthorizationException("Authorization failed! User has not logged in."); + throw new AbpAuthorizationException("Authorization failed! Given roles has not granted: " + authorizationAttribute.Roles); } } else { - await _authorizationService.CheckAsync(authorizationAttribute.Policy).ConfigureAwait(false); + //TODO: Can we find a better, unified, way of checking if current request has been authenticated + if (!_currentUser.IsAuthenticated && !_currentClient.IsAuthenticated) + { + throw new AbpAuthorizationException("Authorization failed! User has not logged in."); + } } - //TODO: What about roles and other props? } } } \ No newline at end of file From 5016ba69a3cfc4e87e9db5fe651325cafcacc766 Mon Sep 17 00:00:00 2001 From: Mehmet Perk Date: Thu, 16 Jan 2020 23:33:05 +0300 Subject: [PATCH 17/62] authorization tests with role --- .../Volo/Abp/Authorization/Authorization_Tests.cs | 9 +++++++++ .../Authorization/TestServices/IMyAuthorizedService1.cs | 2 ++ .../Authorization/TestServices/MyAuthorizedService1.cs | 6 ++++++ 3 files changed, 17 insertions(+) diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs index 2950c7c92a..3968d1baf5 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs @@ -52,5 +52,14 @@ namespace Volo.Abp.Authorization { _permissionDefinitionManager.GetGroups().Count.ShouldBe(1); } + + [Fact] + public async Task Should_Not_Allow_To_Call_Method_If_Has_No_Role_ProtectedByRole_Async() + { + await Assert.ThrowsAsync(async () => + { + await _myAuthorizedService1.ProtectedByRole().ConfigureAwait(false); + }).ConfigureAwait(false); + } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs index b3841c4fed..077934c0d2 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs @@ -11,5 +11,7 @@ namespace Volo.Abp.Authorization.TestServices Task ProtectedByClass(); Task ProtectedByClassAsync(); + + Task ProtectedByRole(); } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs index 6d64ed80dd..07ccfa7deb 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs @@ -30,5 +30,11 @@ namespace Volo.Abp.Authorization.TestServices await Task.Delay(10).ConfigureAwait(false); return 42; } + + [Authorize(Roles = "MyRole")] + public virtual Task ProtectedByRole() + { + return Task.FromResult(42); + } } } \ No newline at end of file From 85f956f5aed8b50f5949fcd9ac325607f8a23d4d Mon Sep 17 00:00:00 2001 From: mperk Date: Sun, 19 Jan 2020 01:03:21 +0300 Subject: [PATCH 18/62] authorize check with combine --- .../MethodInvocationAuthorizationService.cs | 29 +++++-------------- .../Abp/Authorization/Authorization_Tests.cs | 16 ++++++++++ .../TestServices/IMyAuthorizedService1.cs | 2 ++ .../TestServices/MyAuthorizedService1.cs | 7 +++++ 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs index 82a0f9c2d1..b2e06c0207 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs @@ -14,15 +14,18 @@ namespace Volo.Abp.Authorization private readonly IAuthorizationService _authorizationService; private readonly ICurrentUser _currentUser; private readonly ICurrentClient _currentClient; + private readonly IAbpAuthorizationPolicyProvider _abpAuthorizationPolicyProvider; public MethodInvocationAuthorizationService( IAuthorizationService authorizationService, ICurrentUser currentUser, - ICurrentClient currentClient) + ICurrentClient currentClient, + IAbpAuthorizationPolicyProvider abpAuthorizationPolicyProvider) { _authorizationService = authorizationService; _currentUser = currentUser; _currentClient = currentClient; + _abpAuthorizationPolicyProvider = abpAuthorizationPolicyProvider; } public async Task CheckAsync(MethodInvocationAuthorizationContext context) @@ -64,26 +67,10 @@ namespace Volo.Abp.Authorization protected async Task CheckAsync(IAuthorizeData authorizationAttribute) { - if (authorizationAttribute.Policy != null) - { - await _authorizationService.CheckAsync(authorizationAttribute.Policy).ConfigureAwait(false); - } - else if (authorizationAttribute.Roles != null) - { - if(_currentUser.IsInRole(authorizationAttribute.Roles) == false) - { - throw new AbpAuthorizationException("Authorization failed! Given roles has not granted: " + authorizationAttribute.Roles); - } - } - else - { - //TODO: Can we find a better, unified, way of checking if current request has been authenticated - if (!_currentUser.IsAuthenticated && !_currentClient.IsAuthenticated) - { - throw new AbpAuthorizationException("Authorization failed! User has not logged in."); - } - } - + var authorizationPolicy = await AuthorizationPolicy.CombineAsync( + _abpAuthorizationPolicyProvider, + new List { authorizationAttribute }); + await _authorizationService.CheckAsync(authorizationPolicy).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs index 3968d1baf5..827f7b676e 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs @@ -61,5 +61,21 @@ namespace Volo.Abp.Authorization await _myAuthorizedService1.ProtectedByRole().ConfigureAwait(false); }).ConfigureAwait(false); } + + [Fact] + public async Task Should_Allow_To_Call_Method_If_Has_No_Role_ProtectedByRole_Async() + { + int result = await _myAuthorizedService1.ProtectedByRole().ConfigureAwait(false); + result.ShouldBe(42); + } + + [Fact] + public async Task Should_Not_Allow_To_Call_Method_If_Has_No_Role_ProtectedByScheme_Async() + { + await Assert.ThrowsAsync(async () => + { + await _myAuthorizedService1.ProtectedByScheme().ConfigureAwait(false); + }).ConfigureAwait(false); + } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs index 077934c0d2..6c4042168c 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs @@ -13,5 +13,7 @@ namespace Volo.Abp.Authorization.TestServices Task ProtectedByClassAsync(); Task ProtectedByRole(); + + Task ProtectedByScheme(); } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs index 07ccfa7deb..a25227babf 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs @@ -36,5 +36,12 @@ namespace Volo.Abp.Authorization.TestServices { return Task.FromResult(42); } + + [Authorize(AuthenticationSchemes = "Bearer")] + [Authorize(Roles = "MyRole")] + public virtual Task ProtectedByScheme() + { + return Task.FromResult(42); + } } } \ No newline at end of file From 9617efc5e977c1894aeff00f423643a53d0fda90 Mon Sep 17 00:00:00 2001 From: mperk Date: Mon, 20 Jan 2020 15:07:39 +0300 Subject: [PATCH 19/62] change authorization service to abpauthorization service --- .../MethodInvocationAuthorizationService.cs | 16 +++++----------- .../Abp/Authorization/Authorization_Tests.cs | 7 ------- 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs index b2e06c0207..4d0589486b 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs @@ -11,21 +11,15 @@ namespace Volo.Abp.Authorization { public class MethodInvocationAuthorizationService : IMethodInvocationAuthorizationService, ITransientDependency { - private readonly IAuthorizationService _authorizationService; - private readonly ICurrentUser _currentUser; - private readonly ICurrentClient _currentClient; private readonly IAbpAuthorizationPolicyProvider _abpAuthorizationPolicyProvider; + private readonly IAbpAuthorizationService _abpAuthorizationService; public MethodInvocationAuthorizationService( - IAuthorizationService authorizationService, - ICurrentUser currentUser, - ICurrentClient currentClient, - IAbpAuthorizationPolicyProvider abpAuthorizationPolicyProvider) + IAbpAuthorizationPolicyProvider abpAuthorizationPolicyProvider, + IAbpAuthorizationService abpAuthorizationService) { - _authorizationService = authorizationService; - _currentUser = currentUser; - _currentClient = currentClient; _abpAuthorizationPolicyProvider = abpAuthorizationPolicyProvider; + _abpAuthorizationService = abpAuthorizationService; } public async Task CheckAsync(MethodInvocationAuthorizationContext context) @@ -70,7 +64,7 @@ namespace Volo.Abp.Authorization var authorizationPolicy = await AuthorizationPolicy.CombineAsync( _abpAuthorizationPolicyProvider, new List { authorizationAttribute }); - await _authorizationService.CheckAsync(authorizationPolicy).ConfigureAwait(false); + await _abpAuthorizationService.CheckAsync(authorizationPolicy).ConfigureAwait(false); } } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs index 827f7b676e..9be90dc045 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs @@ -62,13 +62,6 @@ namespace Volo.Abp.Authorization }).ConfigureAwait(false); } - [Fact] - public async Task Should_Allow_To_Call_Method_If_Has_No_Role_ProtectedByRole_Async() - { - int result = await _myAuthorizedService1.ProtectedByRole().ConfigureAwait(false); - result.ShouldBe(42); - } - [Fact] public async Task Should_Not_Allow_To_Call_Method_If_Has_No_Role_ProtectedByScheme_Async() { From cfba92293294b523fe1683ab9c1d309d1a7e73f9 Mon Sep 17 00:00:00 2001 From: Mehmet Perk Date: Mon, 20 Jan 2020 21:04:31 +0300 Subject: [PATCH 20/62] authorization tests with role --- .../Authorization/AuthorizationTestBase.cs | 25 +++++++++++++++-- .../Abp/Authorization/Authorization_Tests.cs | 14 ++++++++-- .../TestServices/IMyAuthorizedService1.cs | 3 --- .../IMyAuthorizedServiceWithRole.cs | 13 +++++++++ .../TestServices/MyAuthorizedService1.cs | 15 +---------- .../MyAuthorizedServiceWithRole.cs | 27 +++++++++++++++++++ 6 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedServiceWithRole.cs create mode 100644 framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedServiceWithRole.cs diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AuthorizationTestBase.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AuthorizationTestBase.cs index 8ab051d97e..5cf9976e46 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AuthorizationTestBase.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AuthorizationTestBase.cs @@ -1,4 +1,10 @@ -using Volo.Abp.Testing; +using Microsoft.Extensions.DependencyInjection; +using NSubstitute; +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading; +using Volo.Abp.Security.Claims; +using Volo.Abp.Testing; namespace Volo.Abp.Authorization { @@ -8,5 +14,20 @@ namespace Volo.Abp.Authorization { options.UseAutofac(); } + + protected override void AfterAddApplication(IServiceCollection services) + { + var claims = new List() { + new Claim(AbpClaimTypes.UserName, "Douglas"), + new Claim(AbpClaimTypes.UserId, "1fcf46b2-28c3-48d0-8bac-fa53268a2775"), + new Claim(AbpClaimTypes.Role, "MyRole") + }; + + var identity = new ClaimsIdentity(claims); + var claimsPrincipal = new ClaimsPrincipal(identity); + var principalAccessor = Substitute.For(); + principalAccessor.Principal.Returns(ci => claimsPrincipal); + Thread.CurrentPrincipal = claimsPrincipal; + } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs index 9be90dc045..e886d2f1c4 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs @@ -9,11 +9,13 @@ namespace Volo.Abp.Authorization public class Authorization_Tests : AuthorizationTestBase { private readonly IMyAuthorizedService1 _myAuthorizedService1; + private readonly IMyAuthorizedServiceWithRole _myAuthorizedServiceWithRole; private readonly IPermissionDefinitionManager _permissionDefinitionManager; public Authorization_Tests() { _myAuthorizedService1 = GetRequiredService(); + _myAuthorizedServiceWithRole = GetRequiredService(); _permissionDefinitionManager = GetRequiredService(); } @@ -58,16 +60,24 @@ namespace Volo.Abp.Authorization { await Assert.ThrowsAsync(async () => { - await _myAuthorizedService1.ProtectedByRole().ConfigureAwait(false); + await _myAuthorizedServiceWithRole.ProtectedByAnotherRole().ConfigureAwait(false); }).ConfigureAwait(false); } + [Fact] + public async Task Should_Allow_To_Call_Method_If_Has_No_Role_ProtectedByRole_Async() + { + int result = await _myAuthorizedServiceWithRole.ProtectedByRole().ConfigureAwait(false); + result.ShouldBe(42); + } + + [Fact] public async Task Should_Not_Allow_To_Call_Method_If_Has_No_Role_ProtectedByScheme_Async() { await Assert.ThrowsAsync(async () => { - await _myAuthorizedService1.ProtectedByScheme().ConfigureAwait(false); + await _myAuthorizedServiceWithRole.ProtectedByScheme().ConfigureAwait(false); }).ConfigureAwait(false); } } diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs index 6c4042168c..4c693993cf 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs @@ -12,8 +12,5 @@ namespace Volo.Abp.Authorization.TestServices Task ProtectedByClassAsync(); - Task ProtectedByRole(); - - Task ProtectedByScheme(); } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedServiceWithRole.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedServiceWithRole.cs new file mode 100644 index 0000000000..52b860d774 --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedServiceWithRole.cs @@ -0,0 +1,13 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Authorization.TestServices +{ + public interface IMyAuthorizedServiceWithRole + { + Task ProtectedByRole(); + + Task ProtectedByScheme(); + + Task ProtectedByAnotherRole(); + } +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs index a25227babf..02f234fe99 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedService1.cs @@ -30,18 +30,5 @@ namespace Volo.Abp.Authorization.TestServices await Task.Delay(10).ConfigureAwait(false); return 42; } - - [Authorize(Roles = "MyRole")] - public virtual Task ProtectedByRole() - { - return Task.FromResult(42); - } - - [Authorize(AuthenticationSchemes = "Bearer")] - [Authorize(Roles = "MyRole")] - public virtual Task ProtectedByScheme() - { - return Task.FromResult(42); - } } -} \ No newline at end of file +} diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedServiceWithRole.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedServiceWithRole.cs new file mode 100644 index 0000000000..d660b040c1 --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MyAuthorizedServiceWithRole.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Authorization; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Authorization.TestServices +{ + [Authorize(Roles = "MyRole")] + public class MyAuthorizedServiceWithRole : IMyAuthorizedServiceWithRole, ITransientDependency + { + public virtual Task ProtectedByRole() + { + return Task.FromResult(42); + } + + [Authorize(Roles = "MyAnotherRole")] + public virtual Task ProtectedByAnotherRole() + { + return Task.FromResult(42); + } + + [Authorize(AuthenticationSchemes = "Bearer")] + public virtual Task ProtectedByScheme() + { + return Task.FromResult(42); + } + } +} From 583cdbac1cba2c7ad756200d469bb4470f97294c Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 21 Jan 2020 10:08:17 +0800 Subject: [PATCH 21/62] Check all authorization attributes at the same time. --- .../MethodInvocationAuthorizationService.cs | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs index 4d0589486b..b0d53355fc 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs @@ -3,9 +3,7 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Authorization; -using Volo.Abp.Clients; using Volo.Abp.DependencyInjection; -using Volo.Abp.Users; namespace Volo.Abp.Authorization { @@ -29,10 +27,10 @@ namespace Volo.Abp.Authorization return; } - foreach (var authorizationAttribute in GetAuthorizationDataAttributes(context.Method)) - { - await CheckAsync(authorizationAttribute).ConfigureAwait(false); - } + var authorizationPolicy = await AuthorizationPolicy.CombineAsync(_abpAuthorizationPolicyProvider, + GetAuthorizationDataAttributes(context.Method)).ConfigureAwait(false); + + await _abpAuthorizationService.CheckAsync(authorizationPolicy).ConfigureAwait(false); } protected virtual bool AllowAnonymous(MethodInvocationAuthorizationContext context) @@ -46,7 +44,7 @@ namespace Volo.Abp.Authorization .GetCustomAttributes(true) .OfType(); - if (methodInfo.IsPublic) + if (methodInfo.IsPublic && methodInfo.DeclaringType != null) { attributes = attributes .Union( @@ -58,13 +56,5 @@ namespace Volo.Abp.Authorization return attributes; } - - protected async Task CheckAsync(IAuthorizeData authorizationAttribute) - { - var authorizationPolicy = await AuthorizationPolicy.CombineAsync( - _abpAuthorizationPolicyProvider, - new List { authorizationAttribute }); - await _abpAuthorizationService.CheckAsync(authorizationPolicy).ConfigureAwait(false); - } } } \ No newline at end of file From 9f2b49c2d5a6c365845cf93e1e8caa13a610408d Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 19 Feb 2020 16:42:05 +0800 Subject: [PATCH 22/62] Fix build error. --- .../Abp/Authorization/MethodInvocationAuthorizationService.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs index 6518b7ad75..32fd611b9a 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs @@ -27,7 +27,8 @@ namespace Volo.Abp.Authorization return; } - var authorizationPolicy = await AuthorizationPolicy.CombineAsync(_abpAuthorizationPolicyProvider, GetAuthorizationDataAttributes(context.Method)) + var authorizationPolicy = await AuthorizationPolicy.CombineAsync(_abpAuthorizationPolicyProvider, + GetAuthorizationDataAttributes(context.Method)); await _abpAuthorizationService.CheckAsync(authorizationPolicy); } From 88b8f6ed836f2675ad562c27cb759ee20848d824 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 4 Mar 2020 09:18:39 +0800 Subject: [PATCH 23/62] Check if authorizationPolicy is null. --- .../Authorization/MethodInvocationAuthorizationService.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs index 32fd611b9a..6293c0d034 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs @@ -29,6 +29,11 @@ namespace Volo.Abp.Authorization var authorizationPolicy = await AuthorizationPolicy.CombineAsync(_abpAuthorizationPolicyProvider, GetAuthorizationDataAttributes(context.Method)); + if (authorizationPolicy == null) + { + return; + } + await _abpAuthorizationService.CheckAsync(authorizationPolicy); } From f2d3b712247111a6c869fb85457dca734b7b2985 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 25 Mar 2020 18:08:13 +0800 Subject: [PATCH 24/62] Introducing DynamicProxyIgnoreTypes. Resolve #3180 --- .../AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs | 6 +++++ .../Auditing/AuditingInterceptorRegistrar.cs | 3 ++- .../AuthorizationInterceptorRegistrar.cs | 3 ++- .../DynamicProxy/DynamicProxyIgnoreTypes.cs | 24 +++++++++++++++++++ .../Features/FeatureInterceptorRegistrar.cs | 3 ++- .../Abp/Uow/UnitOfWorkInterceptorRegistrar.cs | 3 ++- .../ValidationInterceptorRegistrar.cs | 3 ++- .../AbpAuthorizationTestModule.cs | 4 +++- ...plicationService_FluentValidation_Tests.cs | 4 +++- .../ApplicationService_Validation_Tests.cs | 4 +++- 10 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs 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 21a675ddc4..e609b0a601 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 @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Hosting; using Volo.Abp.ApiVersioning; using Volo.Abp.AspNetCore.Mvc.Conventions; @@ -20,6 +21,7 @@ using Volo.Abp.AspNetCore.Mvc.Json; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.VirtualFileSystem; using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; using Volo.Abp.Http.Modeling; using Volo.Abp.Localization; using Volo.Abp.Modularity; @@ -38,6 +40,10 @@ namespace Volo.Abp.AspNetCore.Mvc { public override void PreConfigureServices(ServiceConfigurationContext context) { + DynamicProxyIgnoreTypes.IgnoreTypes.AddIfNotContains(typeof(ControllerBase)); + DynamicProxyIgnoreTypes.IgnoreTypes.AddIfNotContains(typeof(PageModel)); + DynamicProxyIgnoreTypes.IgnoreTypes.AddIfNotContains(typeof(ViewComponent)); + context.Services.AddConventionalRegistrar(new AbpAspNetCoreMvcConventionalRegistrar()); } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs index 94920fe1be..6790995331 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; namespace Volo.Abp.Auditing { @@ -8,7 +9,7 @@ namespace Volo.Abp.Auditing { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (ShouldIntercept(context.ImplementationType)) + if (ShouldIntercept(context.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(context.ImplementationType)) { context.Interceptors.TryAdd(); } diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs index 69a285ddc8..b44c299b28 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Authorization; using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; namespace Volo.Abp.Authorization { @@ -10,7 +11,7 @@ namespace Volo.Abp.Authorization { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (ShouldIntercept(context.ImplementationType)) + if (ShouldIntercept(context.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(context.ImplementationType)) { context.Interceptors.TryAdd(); } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs new file mode 100644 index 0000000000..5eae6ef092 --- /dev/null +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Volo.Abp.DynamicProxy +{ + /// + /// Castle's dynamic proxy class feature will have performance issues for some components, such as the controller of Asp net core MVC. + /// For related discussions, see: https://github.com/castleproject/Core/issues/486 https://github.com/abpframework/abp/issues/3180 + /// The Abp framework may enable interceptors for certain components (UOW, Auditing, Authorization, etc.), which requires dynamic proxy classes, but will cause application performance to decline. + /// We need to use other methods for the controller to implement interception, such as middleware or MVC / Page filters. + /// So we provide some ignored types to avoid enabling dynamic proxy classes. + /// By default it is empty. When you use middleware or filters for these components in your application, you can add these types to the list. + /// + public static class DynamicProxyIgnoreTypes + { + public static List IgnoreTypes { get; } = new List(); + + public static bool Contains(Type type, bool includeDerivedTypes = true) + { + return includeDerivedTypes ? IgnoreTypes.Any(t => t.IsAssignableFrom(type)) : IgnoreTypes.Contains(type); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs index 8cb6cd7ea5..b2caa734b5 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Reflection; using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; namespace Volo.Abp.Features { @@ -9,7 +10,7 @@ namespace Volo.Abp.Features { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (ShouldIntercept(context.ImplementationType)) + if (ShouldIntercept(context.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(context.ImplementationType)) { context.Interceptors.TryAdd(); } diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkInterceptorRegistrar.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkInterceptorRegistrar.cs index 849ca8ffd8..c01e760d58 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkInterceptorRegistrar.cs @@ -1,5 +1,6 @@ using System.Reflection; using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; namespace Volo.Abp.Uow { @@ -7,7 +8,7 @@ namespace Volo.Abp.Uow { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (UnitOfWorkHelper.IsUnitOfWorkType(context.ImplementationType.GetTypeInfo())) + if (UnitOfWorkHelper.IsUnitOfWorkType(context.ImplementationType.GetTypeInfo()) && !DynamicProxyIgnoreTypes.Contains(context.ImplementationType)) { context.Interceptors.TryAdd(); } diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptorRegistrar.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptorRegistrar.cs index 81f6eb6597..187f64436e 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptorRegistrar.cs @@ -1,4 +1,5 @@ using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; namespace Volo.Abp.Validation { @@ -6,7 +7,7 @@ namespace Volo.Abp.Validation { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (typeof(IValidationEnabled).IsAssignableFrom(context.ImplementationType)) + if (typeof(IValidationEnabled).IsAssignableFrom(context.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(context.ImplementationType)) { context.Interceptors.TryAdd(); } diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs index 51337ae871..492a36f8b9 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/AbpAuthorizationTestModule.cs @@ -1,6 +1,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Authorization.TestServices; using Volo.Abp.Autofac; +using Volo.Abp.DynamicProxy; using Volo.Abp.Modularity; namespace Volo.Abp.Authorization @@ -13,7 +14,8 @@ namespace Volo.Abp.Authorization { context.Services.OnRegistred(onServiceRegistredContext => { - if (typeof(IMyAuthorizedService1).IsAssignableFrom(onServiceRegistredContext.ImplementationType)) + if (typeof(IMyAuthorizedService1).IsAssignableFrom(onServiceRegistredContext.ImplementationType) && + !DynamicProxyIgnoreTypes.Contains(onServiceRegistredContext.ImplementationType)) { onServiceRegistredContext.Interceptors.TryAdd(); } diff --git a/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs index 5bf11b6d5e..a93eabda1d 100644 --- a/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs +++ b/framework/test/Volo.Abp.FluentValidation.Tests/Volo/Abp/FluentValidation/ApplicationService_FluentValidation_Tests.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Shouldly; using Volo.Abp.Autofac; using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; using Volo.Abp.Modularity; using Volo.Abp.Testing; using Volo.Abp.Validation; @@ -108,7 +109,8 @@ namespace Volo.Abp.FluentValidation { context.Services.OnRegistred(onServiceRegistredContext => { - if (typeof(IMyAppService).IsAssignableFrom(onServiceRegistredContext.ImplementationType)) + if (typeof(IMyAppService).IsAssignableFrom(onServiceRegistredContext.ImplementationType) && + !DynamicProxyIgnoreTypes.Contains(onServiceRegistredContext.ImplementationType)) { onServiceRegistredContext.Interceptors.TryAdd(); } diff --git a/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs b/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs index 0d13867819..de9067e2f7 100644 --- a/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs +++ b/framework/test/Volo.Abp.Validation.Tests/Volo/Abp/Validation/ApplicationService_Validation_Tests.cs @@ -8,6 +8,7 @@ using Shouldly; using Volo.Abp.Application.Dtos; using Volo.Abp.Autofac; using Volo.Abp.DependencyInjection; +using Volo.Abp.DynamicProxy; using Volo.Abp.Modularity; using Volo.Abp.Testing; using Xunit; @@ -198,7 +199,8 @@ namespace Volo.Abp.Validation { context.Services.OnRegistred(onServiceRegistredContext => { - if (typeof(IMyAppService).IsAssignableFrom(onServiceRegistredContext.ImplementationType)) + if (typeof(IMyAppService).IsAssignableFrom(onServiceRegistredContext.ImplementationType) && + !DynamicProxyIgnoreTypes.Contains(onServiceRegistredContext.ImplementationType)) { onServiceRegistredContext.Interceptors.TryAdd(); } From 1475940d853379a867bef3d899c9000199144a95 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 27 Mar 2020 10:24:51 +0800 Subject: [PATCH 25/62] Refactor. --- .../Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs | 8 ++++---- .../Abp/Auditing/AuditingInterceptorRegistrar.cs | 7 ++++++- .../AuthorizationInterceptorRegistrar.cs | 6 +++--- .../Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs | 12 +++++++++--- .../Volo/Abp/Features/FeatureInterceptorRegistrar.cs | 7 ++++--- .../Volo/Abp/Uow/UnitOfWorkInterceptorRegistrar.cs | 10 ++++++++-- .../Abp/Validation/ValidationInterceptorRegistrar.cs | 10 ++++++++-- 7 files changed, 42 insertions(+), 18 deletions(-) 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 e609b0a601..668e7a77dc 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 @@ -40,10 +40,10 @@ namespace Volo.Abp.AspNetCore.Mvc { public override void PreConfigureServices(ServiceConfigurationContext context) { - DynamicProxyIgnoreTypes.IgnoreTypes.AddIfNotContains(typeof(ControllerBase)); - DynamicProxyIgnoreTypes.IgnoreTypes.AddIfNotContains(typeof(PageModel)); - DynamicProxyIgnoreTypes.IgnoreTypes.AddIfNotContains(typeof(ViewComponent)); - + DynamicProxyIgnoreTypes.Add(); + DynamicProxyIgnoreTypes.Add(); + DynamicProxyIgnoreTypes.Add(); + context.Services.AddConventionalRegistrar(new AbpAspNetCoreMvcConventionalRegistrar()); } diff --git a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs index 6790995331..a407335f3d 100644 --- a/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Auditing/Volo/Abp/Auditing/AuditingInterceptorRegistrar.cs @@ -9,7 +9,7 @@ namespace Volo.Abp.Auditing { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (ShouldIntercept(context.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(context.ImplementationType)) + if (ShouldIntercept(context.ImplementationType)) { context.Interceptors.TryAdd(); } @@ -17,6 +17,11 @@ namespace Volo.Abp.Auditing private static bool ShouldIntercept(Type type) { + if (DynamicProxyIgnoreTypes.Contains(type)) + { + return false; + } + if (ShouldAuditTypeByDefault(type)) { return true; diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs index b44c299b28..e33a8fca7e 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/AuthorizationInterceptorRegistrar.cs @@ -11,7 +11,7 @@ namespace Volo.Abp.Authorization { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (ShouldIntercept(context.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(context.ImplementationType)) + if (ShouldIntercept(context.ImplementationType)) { context.Interceptors.TryAdd(); } @@ -19,8 +19,8 @@ namespace Volo.Abp.Authorization private static bool ShouldIntercept(Type type) { - return type.IsDefined(typeof(AuthorizeAttribute), true) || - AnyMethodHasAuthorizeAttribute(type); + return !DynamicProxyIgnoreTypes.Contains(type) && + (type.IsDefined(typeof(AuthorizeAttribute), true) || AnyMethodHasAuthorizeAttribute(type)); } private static bool AnyMethodHasAuthorizeAttribute(Type implementationType) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs index 5eae6ef092..6cdefb6705 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Volo.Abp.Threading; namespace Volo.Abp.DynamicProxy { @@ -14,11 +15,16 @@ namespace Volo.Abp.DynamicProxy /// public static class DynamicProxyIgnoreTypes { - public static List IgnoreTypes { get; } = new List(); - + private static HashSet IgnoredTypes { get; } = new HashSet(); + + public static void Add() + { + IgnoredTypes.Locking(() => IgnoredTypes.AddIfNotContains(typeof(T))); + } + public static bool Contains(Type type, bool includeDerivedTypes = true) { - return includeDerivedTypes ? IgnoreTypes.Any(t => t.IsAssignableFrom(type)) : IgnoreTypes.Contains(type); + return includeDerivedTypes ? IgnoredTypes.Any(t => t.IsAssignableFrom(type)) : IgnoredTypes.Contains(type); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs index b2caa734b5..c20b98c143 100644 --- a/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Features/Volo/Abp/Features/FeatureInterceptorRegistrar.cs @@ -10,7 +10,7 @@ namespace Volo.Abp.Features { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (ShouldIntercept(context.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(context.ImplementationType)) + if (ShouldIntercept(context.ImplementationType)) { context.Interceptors.TryAdd(); } @@ -18,8 +18,9 @@ namespace Volo.Abp.Features private static bool ShouldIntercept(Type type) { - return type.IsDefined(typeof(RequiresFeatureAttribute), true) || - AnyMethodHasRequiresFeatureAttribute(type); + return !DynamicProxyIgnoreTypes.Contains(type) && + (type.IsDefined(typeof(RequiresFeatureAttribute), true) || + AnyMethodHasRequiresFeatureAttribute(type)); } private static bool AnyMethodHasRequiresFeatureAttribute(Type implementationType) diff --git a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkInterceptorRegistrar.cs b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkInterceptorRegistrar.cs index c01e760d58..5331cb1d0e 100644 --- a/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Uow/Volo/Abp/Uow/UnitOfWorkInterceptorRegistrar.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System; +using System.Reflection; using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; @@ -8,10 +9,15 @@ namespace Volo.Abp.Uow { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (UnitOfWorkHelper.IsUnitOfWorkType(context.ImplementationType.GetTypeInfo()) && !DynamicProxyIgnoreTypes.Contains(context.ImplementationType)) + if (ShouldIntercept(context.ImplementationType)) { context.Interceptors.TryAdd(); } } + + private static bool ShouldIntercept(Type type) + { + return !DynamicProxyIgnoreTypes.Contains(type) && UnitOfWorkHelper.IsUnitOfWorkType(type.GetTypeInfo()); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptorRegistrar.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptorRegistrar.cs index 187f64436e..1474efd710 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptorRegistrar.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptorRegistrar.cs @@ -1,4 +1,5 @@ -using Volo.Abp.DependencyInjection; +using System; +using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; namespace Volo.Abp.Validation @@ -7,10 +8,15 @@ namespace Volo.Abp.Validation { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { - if (typeof(IValidationEnabled).IsAssignableFrom(context.ImplementationType) && !DynamicProxyIgnoreTypes.Contains(context.ImplementationType)) + if (ShouldIntercept(context.ImplementationType)) { context.Interceptors.TryAdd(); } } + + private static bool ShouldIntercept(Type type) + { + return !DynamicProxyIgnoreTypes.Contains(type) && typeof(IValidationEnabled).IsAssignableFrom(type); + } } } \ No newline at end of file From 0b1606f40bc9d207b73844ad56b2c7cf43806c2e Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 27 Mar 2020 11:20:17 +0800 Subject: [PATCH 26/62] Add AbpAuditPageFilter, AbpFeaturePageFilter, AbpUowPageFilter, AbpExceptionPageFilter. --- .../ActionDescriptorExtensions.cs | 31 +++++ .../AspNetCore/Mvc/AbpMvcOptionsExtensions.cs | 13 +- .../Mvc/Auditing/AbpAuditPageFilter.cs | 103 ++++++++++++++++ .../AbpExceptionPageFilter.cs | 112 ++++++++++++++++++ .../Mvc/Features/AbpFeaturePageFilter.cs | 44 +++++++ .../AspNetCore/Mvc/Uow/AbpUowPageFilter.cs | 105 ++++++++++++++++ 6 files changed, 406 insertions(+), 2 deletions(-) create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs index fb992cb4af..97b5a6c61b 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Reflection; using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.RazorPages; using Volo.Abp; using Volo.Abp.AspNetCore.Mvc; @@ -37,5 +38,35 @@ namespace Microsoft.AspNetCore.Mvc.Abstractions { return actionDescriptor is ControllerActionDescriptor; } + + public static PageActionDescriptor AsPageActionDescriptor(this ActionDescriptor actionDescriptor) + { + if (!actionDescriptor.IsPageAction()) + { + throw new AbpException($"{nameof(actionDescriptor)} should be type of {typeof(PageActionDescriptor).AssemblyQualifiedName}"); + } + + return actionDescriptor as PageActionDescriptor; + } + + public static MethodInfo GetPageActionMethodInfo(this ActionDescriptor actionDescriptor) + { + return actionDescriptor.AsPageActionDescriptor().GetMethodInfo(); + } + + public static Type GetPageActionReturnType(this PageActionDescriptor actionDescriptor) + { + return actionDescriptor.GetPageActionMethodInfo().ReturnType; + } + + public static bool HasObjectResult(this PageActionDescriptor actionDescriptor) + { + return ActionResultHelper.IsObjectResult(actionDescriptor.GetReturnType()); + } + + public static bool IsPageAction(this ActionDescriptor actionDescriptor) + { + return actionDescriptor is PageActionDescriptor; + } } } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs index 4aedf0b087..64d9daca93 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs @@ -16,7 +16,8 @@ namespace Volo.Abp.AspNetCore.Mvc public static void AddAbp(this MvcOptions options, IServiceCollection services) { AddConventions(options, services); - AddFilters(options); + AddActionFilters(options); + AddPageFilters(options); AddModelBinders(options); AddMetadataProviders(options, services); } @@ -26,7 +27,7 @@ namespace Volo.Abp.AspNetCore.Mvc options.Conventions.Add(new AbpServiceConventionWrapper(services)); } - private static void AddFilters(MvcOptions options) + private static void AddActionFilters(MvcOptions options) { options.Filters.AddService(typeof(AbpAuditActionFilter)); options.Filters.AddService(typeof(AbpNoContentActionFilter)); @@ -36,6 +37,14 @@ namespace Volo.Abp.AspNetCore.Mvc options.Filters.AddService(typeof(AbpExceptionFilter)); } + private static void AddPageFilters(MvcOptions options) + { + options.Filters.AddService(typeof(AbpAuditPageFilter)); + options.Filters.AddService(typeof(AbpFeaturePageFilter)); + options.Filters.AddService(typeof(AbpUowPageFilter)); + options.Filters.AddService(typeof(AbpExceptionPageFilter)); + } + private static void AddModelBinders(MvcOptions options) { options.ModelBinderProviders.Insert(0, new AbpDateTimeModelBinderProvider()); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs new file mode 100644 index 0000000000..23e94790e9 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs @@ -0,0 +1,103 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Options; +using Volo.Abp.Aspects; +using Volo.Abp.Auditing; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.AspNetCore.Mvc.Auditing +{ + public class AbpAuditPageFilter : IAsyncPageFilter, ITransientDependency + { + protected AbpAuditingOptions Options { get; } + private readonly IAuditingHelper _auditingHelper; + private readonly IAuditingManager _auditingManager; + + public AbpAuditPageFilter(IOptions options, IAuditingHelper auditingHelper, IAuditingManager auditingManager) + { + Options = options.Value; + _auditingHelper = auditingHelper; + _auditingManager = auditingManager; + } + + public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) + { + return Task.CompletedTask; + } + + public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + if (context.HandlerMethod == null || !ShouldSaveAudit(context, out var auditLog, out var auditLogAction)) + { + await next(); + return; + } + + using (AbpCrossCuttingConcerns.Applying(context.HandlerInstance, AbpCrossCuttingConcerns.Auditing)) + { + var stopwatch = Stopwatch.StartNew(); + + try + { + var result = await next(); + + if (result.Exception != null && !result.ExceptionHandled) + { + auditLog.Exceptions.Add(result.Exception); + } + } + catch (Exception ex) + { + auditLog.Exceptions.Add(ex); + throw; + } + finally + { + stopwatch.Stop(); + auditLogAction.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds); + auditLog.Actions.Add(auditLogAction); + } + } + } + + private bool ShouldSaveAudit(PageHandlerExecutingContext context, out AuditLogInfo auditLog, out AuditLogActionInfo auditLogAction) + { + auditLog = null; + auditLogAction = null; + + if (!Options.IsEnabled) + { + return false; + } + + if (!context.ActionDescriptor.IsPageAction()) + { + return false; + } + + var auditLogScope = _auditingManager.Current; + if (auditLogScope == null) + { + return false; + } + + if (!_auditingHelper.ShouldSaveAudit(context.ActionDescriptor.GetMethodInfo(), true)) + { + return false; + } + + auditLog = auditLogScope.Log; + auditLogAction = _auditingHelper.CreateAuditLogAction( + auditLog, + context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType(), + context.ActionDescriptor.AsControllerActionDescriptor().MethodInfo, + context.HandlerArguments + ); + + return true; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs new file mode 100644 index 0000000000..8aa45688ca --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs @@ -0,0 +1,112 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Volo.Abp.AspNetCore.ExceptionHandling; +using Volo.Abp.DependencyInjection; +using Volo.Abp.ExceptionHandling; +using Volo.Abp.Http; +using Volo.Abp.Json; + +namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling +{ + public class AbpExceptionPageFilter : IAsyncPageFilter, ITransientDependency + { + public ILogger Logger { get; set; } + + private readonly IExceptionToErrorInfoConverter _errorInfoConverter; + private readonly IHttpExceptionStatusCodeFinder _statusCodeFinder; + private readonly IJsonSerializer _jsonSerializer; + + public AbpExceptionPageFilter( + IExceptionToErrorInfoConverter errorInfoConverter, + IHttpExceptionStatusCodeFinder statusCodeFinder, + IJsonSerializer jsonSerializer) + { + _errorInfoConverter = errorInfoConverter; + _statusCodeFinder = statusCodeFinder; + _jsonSerializer = jsonSerializer; + + Logger = NullLogger.Instance; + } + + + public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) + { + return Task.CompletedTask; + } + + public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + if (context.HandlerMethod == null || !ShouldHandleException(context)) + { + await next(); + return; + } + + var pageHandlerExecutedContext = await next(); + if (pageHandlerExecutedContext.Exception == null) + { + return;; + } + + await HandleAndWrapException(pageHandlerExecutedContext); + } + + protected virtual bool ShouldHandleException(PageHandlerExecutingContext context) + { + //TODO: Create DontWrap attribute to control wrapping..? + + if (context.ActionDescriptor.IsPageAction() && + context.ActionDescriptor.HasObjectResult()) + { + return true; + } + + if (context.HttpContext.Request.CanAccept(MimeTypes.Application.Json)) + { + return true; + } + + if (context.HttpContext.Request.IsAjax()) + { + return true; + } + + return false; + } + + protected virtual async Task HandleAndWrapException(PageHandlerExecutedContext context) + { + //TODO: Trigger an AbpExceptionHandled event or something like that. + + context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true"); + context.HttpContext.Response.StatusCode = (int)_statusCodeFinder.GetStatusCode(context.HttpContext, context.Exception); + + var remoteServiceErrorInfo = _errorInfoConverter.Convert(context.Exception); + + context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo)); + + var logLevel = context.Exception.GetLogLevel(); + + Logger.LogWithLevel(logLevel, $"---------- {nameof(RemoteServiceErrorInfo)} ----------"); + Logger.LogWithLevel(logLevel, _jsonSerializer.Serialize(remoteServiceErrorInfo, indented: true)); + Logger.LogException(context.Exception, logLevel); + + await context.HttpContext + .RequestServices + .GetRequiredService() + .NotifyAsync( + new ExceptionNotificationContext(context.Exception) + ); + + context.Exception = null; //Handled! + } + + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs new file mode 100644 index 0000000000..ceb117d2ff --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs @@ -0,0 +1,44 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Filters; +using Volo.Abp.Aspects; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Features; + +namespace Volo.Abp.AspNetCore.Mvc.Features +{ + public class AbpFeaturePageFilter : IAsyncPageFilter, ITransientDependency + { + private readonly IMethodInvocationFeatureCheckerService _methodInvocationAuthorizationService; + + public AbpFeaturePageFilter(IMethodInvocationFeatureCheckerService methodInvocationAuthorizationService) + { + _methodInvocationAuthorizationService = methodInvocationAuthorizationService; + } + + public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) + { + return Task.CompletedTask; + } + + public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + if (context.HandlerMethod == null || !context.ActionDescriptor.IsPageAction()) + { + await next(); + return; + } + + var methodInfo = context.ActionDescriptor.GetMethodInfo(); + + using (AbpCrossCuttingConcerns.Applying(context.HandlerInstance, AbpCrossCuttingConcerns.FeatureChecking)) + { + await _methodInvocationAuthorizationService.CheckAsync( + new MethodInvocationFeatureCheckerContext(methodInfo) + ); + + await next(); + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs new file mode 100644 index 0000000000..aeed982173 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs @@ -0,0 +1,105 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Uow; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Uow; + +namespace Volo.Abp.AspNetCore.Mvc.Uow +{ + public class AbpUowPageFilter : IAsyncPageFilter, ITransientDependency + { + private readonly IUnitOfWorkManager _unitOfWorkManager; + private readonly AbpUnitOfWorkDefaultOptions _defaultOptions; + + public AbpUowPageFilter(IUnitOfWorkManager unitOfWorkManager, IOptions options) + { + _unitOfWorkManager = unitOfWorkManager; + _defaultOptions = options.Value; + } + public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) + { + return Task.CompletedTask; + } + + public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) + { + if (context.HandlerMethod == null || !context.ActionDescriptor.IsPageAction()) + { + await next(); + return; + } + + var methodInfo = context.ActionDescriptor.GetMethodInfo(); + var unitOfWorkAttr = UnitOfWorkHelper.GetUnitOfWorkAttributeOrNull(methodInfo); + + context.HttpContext.Items["_AbpActionInfo"] = new AbpActionInfoInHttpContext + { + IsObjectResult = context.ActionDescriptor.HasObjectResult() + }; + + if (unitOfWorkAttr?.IsDisabled == true) + { + await next(); + return; + } + + var options = CreateOptions(context, unitOfWorkAttr); + + //Trying to begin a reserved UOW by AbpUnitOfWorkMiddleware + if (_unitOfWorkManager.TryBeginReserved(AbpUnitOfWorkMiddleware.UnitOfWorkReservationName, options)) + { + var result = await next(); + if (!Succeed(result)) + { + await RollbackAsync(context); + } + + return; + } + + //Begin a new, independent unit of work + using (var uow = _unitOfWorkManager.Begin(options)) + { + var result = await next(); + if (Succeed(result)) + { + await uow.CompleteAsync(context.HttpContext.RequestAborted); + } + } + } + + private AbpUnitOfWorkOptions CreateOptions(PageHandlerExecutingContext context, UnitOfWorkAttribute unitOfWorkAttribute) + { + var options = new AbpUnitOfWorkOptions(); + + unitOfWorkAttribute?.SetOptions(options); + + if (unitOfWorkAttribute?.IsTransactional == null) + { + options.IsTransactional = _defaultOptions.CalculateIsTransactional( + autoValue: !string.Equals(context.HttpContext.Request.Method, HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase) + ); + } + + return options; + } + + private async Task RollbackAsync(PageHandlerExecutingContext context) + { + var currentUow = _unitOfWorkManager.Current; + if (currentUow != null) + { + await currentUow.RollbackAsync(context.HttpContext.RequestAborted); + } + } + + private static bool Succeed(PageHandlerExecutedContext result) + { + return result.Exception == null || result.ExceptionHandled; + } + } +} \ No newline at end of file From fd5eda2b7f533aa4b0cf51a7384c8ebfd15293da Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 27 Mar 2020 16:32:21 +0800 Subject: [PATCH 27/62] Add unit tests for razor page filters. --- .../ActionDescriptorExtensions.cs | 25 ------- .../AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs | 7 +- .../AspNetCore/Mvc/AbpMvcOptionsExtensions.cs | 2 +- .../Mvc/Auditing/AbpAuditPageFilter.cs | 6 +- .../AbpExceptionPageFilter.cs | 2 +- .../Mvc/Features/AbpFeaturePageFilter.cs | 2 +- .../AspNetCore/Mvc/Uow/AbpUowPageFilter.cs | 4 +- .../Volo.Abp.AspNetCore.Mvc.Tests.csproj | 19 ++++++ .../Mvc/AbpAspNetCoreMvcTestModule.cs | 10 ++- .../Mvc/Auditing/AuditTestController_Tests.cs | 1 + .../Mvc/Auditing/AuditTestPage.cshtml | 19 ++++++ .../Mvc/Auditing/AuditTestPage.cshtml.cs | 33 ++++++++++ .../Mvc/Auditing/AuditTestPage_Tests.cs | 65 +++++++++++++++++++ .../ExceptionTestController_Tests.cs | 4 ++ .../ExceptionTestPage.cshtml | 19 ++++++ .../ExceptionTestPage.cshtml.cs | 18 +++++ .../ExceptionTestPage_Tests.cs | 56 ++++++++++++++++ .../Mvc/Features/FeatureTestPage.cshtml | 19 ++++++ .../Mvc/Features/FeatureTestPage.cshtml.cs | 27 ++++++++ .../Mvc/Features/FeatureTestPage_Tests.cs | 34 ++++++++++ ...WorkPageFilter_Exception_Rollback_Tests.cs | 37 +++++++++++ .../Mvc/Uow/UnitOfWorkPageFilter_Tests.cs | 22 +++++++ .../Mvc/Uow/UnitOfWorkTestPage.cshtml | 19 ++++++ .../Mvc/Uow/UnitOfWorkTestPage.cshtml.cs | 54 +++++++++++++++ 24 files changed, 469 insertions(+), 35 deletions(-) create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage.cshtml create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage.cshtml.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage_Tests.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage_Tests.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkPageFilter_Exception_Rollback_Tests.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkPageFilter_Tests.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs index 97b5a6c61b..f54375c844 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Microsoft/AspNetCore/Mvc/Abstractions/ActionDescriptorExtensions.cs @@ -38,31 +38,6 @@ namespace Microsoft.AspNetCore.Mvc.Abstractions { return actionDescriptor is ControllerActionDescriptor; } - - public static PageActionDescriptor AsPageActionDescriptor(this ActionDescriptor actionDescriptor) - { - if (!actionDescriptor.IsPageAction()) - { - throw new AbpException($"{nameof(actionDescriptor)} should be type of {typeof(PageActionDescriptor).AssemblyQualifiedName}"); - } - - return actionDescriptor as PageActionDescriptor; - } - - public static MethodInfo GetPageActionMethodInfo(this ActionDescriptor actionDescriptor) - { - return actionDescriptor.AsPageActionDescriptor().GetMethodInfo(); - } - - public static Type GetPageActionReturnType(this PageActionDescriptor actionDescriptor) - { - return actionDescriptor.GetPageActionMethodInfo().ReturnType; - } - - public static bool HasObjectResult(this PageActionDescriptor actionDescriptor) - { - return ActionResultHelper.IsObjectResult(actionDescriptor.GetReturnType()); - } public static bool IsPageAction(this ActionDescriptor actionDescriptor) { 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 668e7a77dc..4d970acd45 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 @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Filters; @@ -12,7 +12,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.Extensions.Hosting; using Volo.Abp.ApiVersioning; using Volo.Abp.AspNetCore.Mvc.Conventions; @@ -114,6 +116,9 @@ namespace Volo.Abp.AspNetCore.Mvc //Use DI to create view components context.Services.Replace(ServiceDescriptor.Singleton()); + //Use DI to create razor page + context.Services.Replace(ServiceDescriptor.Singleton()); + //Add feature providers var partManager = context.Services.GetSingletonInstance(); var application = context.Services.GetSingletonInstance(); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs index 64d9daca93..f1225f1abf 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/AbpMvcOptionsExtensions.cs @@ -39,10 +39,10 @@ namespace Volo.Abp.AspNetCore.Mvc private static void AddPageFilters(MvcOptions options) { + options.Filters.AddService(typeof(AbpExceptionPageFilter)); options.Filters.AddService(typeof(AbpAuditPageFilter)); options.Filters.AddService(typeof(AbpFeaturePageFilter)); options.Filters.AddService(typeof(AbpUowPageFilter)); - options.Filters.AddService(typeof(AbpExceptionPageFilter)); } private static void AddModelBinders(MvcOptions options) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs index 23e94790e9..6e9996574c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Auditing/AbpAuditPageFilter.cs @@ -84,7 +84,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Auditing return false; } - if (!_auditingHelper.ShouldSaveAudit(context.ActionDescriptor.GetMethodInfo(), true)) + if (!_auditingHelper.ShouldSaveAudit(context.HandlerMethod.MethodInfo, true)) { return false; } @@ -92,8 +92,8 @@ namespace Volo.Abp.AspNetCore.Mvc.Auditing auditLog = auditLogScope.Log; auditLogAction = _auditingHelper.CreateAuditLogAction( auditLog, - context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType(), - context.ActionDescriptor.AsControllerActionDescriptor().MethodInfo, + context.HandlerMethod.GetType(), + context.HandlerMethod.MethodInfo, context.HandlerArguments ); diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs index 8aa45688ca..fb0a7c5a1f 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionPageFilter.cs @@ -63,7 +63,7 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling //TODO: Create DontWrap attribute to control wrapping..? if (context.ActionDescriptor.IsPageAction() && - context.ActionDescriptor.HasObjectResult()) + ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType)) { return true; } diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs index ceb117d2ff..83e26ace9c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Features/AbpFeaturePageFilter.cs @@ -29,7 +29,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Features return; } - var methodInfo = context.ActionDescriptor.GetMethodInfo(); + var methodInfo = context.HandlerMethod.MethodInfo; using (AbpCrossCuttingConcerns.Applying(context.HandlerInstance, AbpCrossCuttingConcerns.FeatureChecking)) { diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs index aeed982173..993a12b0e7 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/Uow/AbpUowPageFilter.cs @@ -33,12 +33,12 @@ namespace Volo.Abp.AspNetCore.Mvc.Uow return; } - var methodInfo = context.ActionDescriptor.GetMethodInfo(); + var methodInfo = context.HandlerMethod.MethodInfo; var unitOfWorkAttr = UnitOfWorkHelper.GetUnitOfWorkAttributeOrNull(methodInfo); context.HttpContext.Items["_AbpActionInfo"] = new AbpActionInfoInHttpContext { - IsObjectResult = context.ActionDescriptor.HasObjectResult() + IsObjectResult = ActionResultHelper.IsObjectResult(context.HandlerMethod.MethodInfo.ReturnType) }; if (unitOfWorkAttr?.IsDisabled == true) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj index ddb3f5bc44..d47b369902 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj @@ -24,6 +24,7 @@ + @@ -39,6 +40,24 @@ PreserveNewest + + + true + PreserveNewest + + + true + PreserveNewest + + + true + PreserveNewest + + + true + PreserveNewest + + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs index 5e74ec4bfa..39b7d9af5f 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcTestModule.cs @@ -1,6 +1,9 @@ -using System; +using System; +using System.Linq; using Localization.Resources.AbpUi; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Mvc.ApplicationModels; +using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Authorization; using Volo.Abp.AspNetCore.Mvc.Localization; @@ -73,6 +76,11 @@ namespace Volo.Abp.AspNetCore.Mvc options.Languages.Add(new LanguageInfo("en", "en", "English")); options.Languages.Add(new LanguageInfo("tr", "tr", "Türkçe")); }); + + Configure(options => + { + options.RootDirectory = "/Volo/Abp/AspNetCore/Mvc"; + }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController_Tests.cs index 49bc689e14..3f934d5fe8 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestController_Tests.cs @@ -51,6 +51,7 @@ namespace Volo.Abp.AspNetCore.Mvc.Auditing await _auditingStore.Received().SaveAsync(Arg.Any()); } + [Fact] public async Task Should_Trigger_Middleware_And_AuditLog_Exception_When_Returns_Object() { diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage.cshtml new file mode 100644 index 0000000000..bf85af77ef --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage.cshtml @@ -0,0 +1,19 @@ +@page +@model Volo.Abp.AspNetCore.Mvc.Auditing.AuditTestPage + +@{ + Layout = null; +} + + + + + + + + +
+ +
+ + \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage.cshtml.cs new file mode 100644 index 0000000000..6425d6585b --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage.cshtml.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; +using Volo.Abp.Auditing; + +namespace Volo.Abp.AspNetCore.Mvc.Auditing +{ + public class AuditTestPage : AbpPageModel + { + private readonly AbpAuditingOptions _options; + + public AuditTestPage(IOptions options) + { + _options = options.Value; + } + + public IActionResult OnGetAuditSuccessForGetRequests() + { + return new OkResult(); + } + + public IActionResult OnGetAuditFailForGetRequests() + { + throw new UserFriendlyException("Exception occurred!"); + } + + public ObjectResult OnGetAuditFailForGetRequestsReturningObject() + { + throw new UserFriendlyException("Exception occurred!"); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage_Tests.cs new file mode 100644 index 0000000000..7d5298e253 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Auditing/AuditTestPage_Tests.cs @@ -0,0 +1,65 @@ +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using NSubstitute; +using Volo.Abp.Auditing; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Auditing +{ + public class AuditTestPage_Tests : AspNetCoreMvcTestBase + { + private readonly AbpAuditingOptions _options; + private IAuditingStore _auditingStore; + + public AuditTestPage_Tests() + { + _options = ServiceProvider.GetRequiredService>().Value; + _auditingStore = ServiceProvider.GetRequiredService(); + } + + protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) + { + _auditingStore = Substitute.For(); + services.Replace(ServiceDescriptor.Singleton(_auditingStore)); + base.ConfigureServices(context, services); + } + + [Fact] + public async Task Should_Trigger_Middleware_And_AuditLog_Success_For_GetRequests() + { + _options.IsEnabledForGetRequests = true; + _options.AlwaysLogOnException = false; + await GetResponseAsync("/Auditing/AuditTestPage?handler=AuditSuccessForGetRequests"); + await _auditingStore.Received().SaveAsync(Arg.Any()); + } + + [Fact] + public async Task Should_Trigger_Middleware_And_AuditLog_Exception_Always() + { + _options.IsEnabled = true; + _options.AlwaysLogOnException = true; + + try + { + await GetResponseAsync("/Auditing/AuditTestPage?handler=AuditFailForGetRequests", System.Net.HttpStatusCode.Forbidden); + } + catch { } + + await _auditingStore.Received().SaveAsync(Arg.Any()); + } + + [Fact] + public async Task Should_Trigger_Middleware_And_AuditLog_Exception_When_Returns_Object() + { + _options.IsEnabled = true; + _options.AlwaysLogOnException = true; + + await GetResponseAsync("/Auditing/AuditTestPage?handler=AuditFailForGetRequestsReturningObject", System.Net.HttpStatusCode.Forbidden); + + await _auditingStore.Received().SaveAsync(Arg.Any()); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestController_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestController_Tests.cs index 0de46fbe55..46cdd073e7 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestController_Tests.cs +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestController_Tests.cs @@ -30,9 +30,11 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling result.Error.ShouldNotBeNull(); result.Error.Message.ShouldBe("This is a sample exception!"); +#pragma warning disable 4014 _fakeExceptionSubscriber .Received() .HandleAsync(Arg.Any()); +#pragma warning restore 4014 } [Fact] @@ -44,9 +46,11 @@ namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling ) ); +#pragma warning disable 4014 _fakeExceptionSubscriber .DidNotReceive() .HandleAsync(Arg.Any()); +#pragma warning restore 4014 } } } diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml new file mode 100644 index 0000000000..7ba36da037 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml @@ -0,0 +1,19 @@ +@page +@model Volo.Abp.AspNetCore.Mvc.ExceptionHandling.ExceptionTestPage + +@{ + Layout = null; +} + + + + + + + + +
+ +
+ + \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs new file mode 100644 index 0000000000..717d31effe --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage.cshtml.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling +{ + public class ExceptionTestPage : AbpPageModel + { + public void OnGetUserFriendlyException1() + { + throw new UserFriendlyException("This is a sample exception!"); + } + + public IActionResult OnGetUserFriendlyException2() + { + throw new UserFriendlyException("This is a sample exception!"); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs new file mode 100644 index 0000000000..7013c0c676 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/ExceptionTestPage_Tests.cs @@ -0,0 +1,56 @@ +using System.Net; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using NSubstitute; +using Shouldly; +using Volo.Abp.ExceptionHandling; +using Volo.Abp.Http; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling +{ + public class ExceptionTestPage_Tests : AspNetCoreMvcTestBase + { + private IExceptionSubscriber _fakeExceptionSubscriber; + + protected override void ConfigureServices(HostBuilderContext context, IServiceCollection services) + { + base.ConfigureServices(context, services); + + _fakeExceptionSubscriber = Substitute.For(); + + services.AddSingleton(_fakeExceptionSubscriber); + } + + [Fact] + public async Task Should_Return_RemoteServiceErrorResponse_For_UserFriendlyException_For_Void_Return_Value() + { + var result = await GetResponseAsObjectAsync("/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException1", HttpStatusCode.Forbidden); + result.Error.ShouldNotBeNull(); + result.Error.Message.ShouldBe("This is a sample exception!"); + +#pragma warning disable 4014 + _fakeExceptionSubscriber + .Received() + .HandleAsync(Arg.Any()); +#pragma warning restore 4014 + } + + [Fact] + public async Task Should_Not_Handle_Exceptions_For_ActionResult_Return_Values() + { + await Assert.ThrowsAsync( + async () => await GetResponseAsObjectAsync( + "/ExceptionHandling/ExceptionTestPage?handler=UserFriendlyException2" + ) + ); + +#pragma warning disable 4014 + _fakeExceptionSubscriber + .DidNotReceive() + .HandleAsync(Arg.Any()); +#pragma warning restore 4014 + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml new file mode 100644 index 0000000000..8f0450becb --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml @@ -0,0 +1,19 @@ +@page +@model Volo.Abp.AspNetCore.Mvc.Features.FeatureTestPage + +@{ + Layout = null; +} + + + + + + + + +
+ +
+ + \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs new file mode 100644 index 0000000000..6a051a29ee --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage.cshtml.cs @@ -0,0 +1,27 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; +using Volo.Abp.Features; + +namespace Volo.Abp.AspNetCore.Mvc.Features +{ + public class FeatureTestPage : AbpPageModel + { + [RequiresFeature("AllowedFeature")] + public Task OnGetAllowedFeatureAsync() + { + return Task.CompletedTask; + } + + [RequiresFeature("NotAllowedFeature")] + public void OnGetNotAllowedFeature() + { + + } + + public ObjectResult OnGetNoFeature() + { + return new ObjectResult(42); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage_Tests.cs new file mode 100644 index 0000000000..91226f1938 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Features/FeatureTestPage_Tests.cs @@ -0,0 +1,34 @@ +using System.Net; +using System.Threading.Tasks; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Features +{ + public class FeatureTestPage_Tests : AspNetCoreMvcTestBase + { + [Fact] + public async Task Should_Allow_Enabled_Features() + { + await GetResponseAsStringAsync( + "/Features/FeatureTestPage?handler=AllowedFeature" + ); + } + + [Fact] + public async Task Should_Not_Allow_Not_Enabled_Features() + { + await GetResponseAsStringAsync( + "/Features/FeatureTestPage?handler=NotAllowedFeature", + HttpStatusCode.Unauthorized + ); + } + + [Fact] + public async Task Should_Allow_Actions_With_No_Feature() + { + await GetResponseAsStringAsync( + "/Features/FeatureTestPage?handler=NoFeature" + ); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkPageFilter_Exception_Rollback_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkPageFilter_Exception_Rollback_Tests.cs new file mode 100644 index 0000000000..b09d3ca2c9 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkPageFilter_Exception_Rollback_Tests.cs @@ -0,0 +1,37 @@ +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Shouldly; +using Volo.Abp.Http; +using Volo.Abp.Json; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Uow +{ + public class UnitOfWorkPageFilter_Exception_Rollback_Tests : AspNetCoreMvcTestBase + { + [Fact] + public async Task Should_Rollback_Transaction_For_Handled_Exceptions() + { + var result = await GetResponseAsObjectAsync("/Uow/UnitOfWorkTestPage?handler=HandledException", HttpStatusCode.Forbidden); + result.Error.ShouldNotBeNull(); + result.Error.Message.ShouldBe("This is a sample exception!"); + } + + [Fact] + public async Task Should_Gracefully_Handle_Exceptions_On_Complete() + { + var response = await GetResponseAsync("/Uow/UnitOfWorkTestPage?handler=ExceptionOnComplete", HttpStatusCode.Forbidden); + + response.Headers.GetValues(AbpHttpConsts.AbpErrorFormat).FirstOrDefault().ShouldBe("true"); + + var resultAsString = await response.Content.ReadAsStringAsync(); + + var result = ServiceProvider.GetRequiredService().Deserialize(resultAsString); + + result.Error.ShouldNotBeNull(); + result.Error.Message.ShouldBe(TestUnitOfWorkConfig.ExceptionOnCompleteMessage); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkPageFilter_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkPageFilter_Tests.cs new file mode 100644 index 0000000000..820ccf91b7 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkPageFilter_Tests.cs @@ -0,0 +1,22 @@ +using System.Threading.Tasks; +using Shouldly; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Uow +{ + public class UnitOfWorkPageFilter_Tests: AspNetCoreMvcTestBase + { + [Fact] + public async Task Get_Actions_Should_Not_Be_Transactional() + { + await GetResponseAsStringAsync("/Uow/UnitOfWorkTestPage?handler=RequiresUow"); + } + + [Fact] + public async Task Non_Get_Actions_Should_Be_Transactional() + { + var result = await Client.PostAsync("/Uow/UnitOfWorkTestPage?handler=RequiresUow", null); + result.IsSuccessStatusCode.ShouldBeTrue(); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml new file mode 100644 index 0000000000..da30a64818 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml @@ -0,0 +1,19 @@ +@page +@model Volo.Abp.AspNetCore.Mvc.Uow.UnitOfWorkTestPage + +@{ + Layout = null; +} + + + + + + + + +
+ +
+ + \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs new file mode 100644 index 0000000000..ce54e8d34f --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Uow/UnitOfWorkTestPage.cshtml.cs @@ -0,0 +1,54 @@ +using Microsoft.AspNetCore.Mvc; +using Shouldly; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; +using Volo.Abp.Uow; + +namespace Volo.Abp.AspNetCore.Mvc.Uow +{ + [IgnoreAntiforgeryToken] + public class UnitOfWorkTestPage : AbpPageModel + { + private readonly TestUnitOfWorkConfig _testUnitOfWorkConfig; + + public UnitOfWorkTestPage(TestUnitOfWorkConfig testUnitOfWorkConfig) + { + _testUnitOfWorkConfig = testUnitOfWorkConfig; + } + + public IActionResult OnGetRequiresUow() + { + CurrentUnitOfWork.ShouldNotBeNull(); + CurrentUnitOfWork.Options.IsTransactional.ShouldBeFalse(); + + return Content("OK"); + } + + public IActionResult OnPostRequiresUow() + { + CurrentUnitOfWork.ShouldNotBeNull(); + CurrentUnitOfWork.Options.IsTransactional.ShouldBeTrue(); + + return Content("OK"); + } + + [UnitOfWork(isTransactional: true)] + public void OnGetHandledException() + { + CurrentUnitOfWork.ShouldNotBeNull(); + CurrentUnitOfWork.Options.IsTransactional.ShouldBeTrue(); + + throw new UserFriendlyException("This is a sample exception!"); + } + + public ObjectResult OnGetExceptionOnComplete() + { + CurrentUnitOfWork.ShouldNotBeNull(); + CurrentUnitOfWork.Options.IsTransactional.ShouldBeFalse(); + + _testUnitOfWorkConfig.ThrowExceptionOnComplete = true; + + //Prevent rendering of pages. + return new ObjectResult(""); + } + } +} \ No newline at end of file From 406d1cdb9b22c22d028043790d2f0bfc8aed015a Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 27 Mar 2020 16:34:21 +0800 Subject: [PATCH 28/62] Remove ViewComponent in DynamicProxyIgnoreTypes. --- .../Volo/Abp/AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs | 1 - 1 file changed, 1 deletion(-) 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 4d970acd45..b3f70b161d 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 @@ -44,7 +44,6 @@ namespace Volo.Abp.AspNetCore.Mvc { DynamicProxyIgnoreTypes.Add(); DynamicProxyIgnoreTypes.Add(); - DynamicProxyIgnoreTypes.Add(); context.Services.AddConventionalRegistrar(new AbpAspNetCoreMvcConventionalRegistrar()); } From abda1104aed9ec70b2a08e6ced5399b1cdf29f80 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 27 Mar 2020 21:23:30 +0800 Subject: [PATCH 29/62] Add authorization unit test for razor page. --- .../Volo.Abp.AspNetCore.Mvc.Tests.csproj | 4 ++ .../Mvc/Authorization/AuthTestPage.cshtml | 19 +++++++++ .../Mvc/Authorization/AuthTestPage.cshtml.cs | 18 +++++++++ .../Mvc/Authorization/AuthTestPage_Tests.cs | 40 +++++++++++++++++++ 4 files changed, 81 insertions(+) create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage.cshtml create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage.cshtml.cs create mode 100644 framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage_Tests.cs diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj index d47b369902..bb841bc085 100644 --- a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo.Abp.AspNetCore.Mvc.Tests.csproj @@ -57,6 +57,10 @@ true PreserveNewest + + true + PreserveNewest + diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage.cshtml b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage.cshtml new file mode 100644 index 0000000000..cf94310510 --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage.cshtml @@ -0,0 +1,19 @@ +@page +@model Volo.Abp.AspNetCore.Mvc.Authorization.AuthTestPage + +@{ + Layout = null; +} + + + + + + + + +
+ +
+ + \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage.cshtml.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage.cshtml.cs new file mode 100644 index 0000000000..5ae66edb5f --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage.cshtml.cs @@ -0,0 +1,18 @@ +using System; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Volo.Abp.AspNetCore.Mvc.UI.RazorPages; + +namespace Volo.Abp.AspNetCore.Mvc.Authorization +{ + [Authorize] + public class AuthTestPage : AbpPageModel + { + public static Guid FakeUserId { get; } = Guid.NewGuid(); + + public ActionResult OnGet() + { + return Content("OK"); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage_Tests.cs b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage_Tests.cs new file mode 100644 index 0000000000..ec8a043fbe --- /dev/null +++ b/framework/test/Volo.Abp.AspNetCore.Mvc.Tests/Volo/Abp/AspNetCore/Mvc/Authorization/AuthTestPage_Tests.cs @@ -0,0 +1,40 @@ +using System.Security.Claims; +using System.Threading.Tasks; +using Shouldly; +using Volo.Abp.AspNetCore.TestBase; +using Volo.Abp.Autofac; +using Volo.Abp.MemoryDb; +using Volo.Abp.Modularity; +using Volo.Abp.Security.Claims; +using Xunit; + +namespace Volo.Abp.AspNetCore.Mvc.Authorization +{ + [DependsOn( + typeof(AbpAspNetCoreTestBaseModule), + typeof(AbpMemoryDbTestModule), + typeof(AbpAspNetCoreMvcModule), + typeof(AbpAutofacModule) + )] + public class AuthTestPage_Tests: AspNetCoreMvcTestBase + { + private readonly FakeUserClaims _fakeRequiredService; + + public AuthTestPage_Tests() + { + _fakeRequiredService = GetRequiredService(); + } + + [Fact] + public async Task Should_Call_Simple_Authorized_Method_With_Authenticated_User() + { + _fakeRequiredService.Claims.AddRange(new[] + { + new Claim(AbpClaimTypes.UserId, AuthTestController.FakeUserId.ToString()) + }); + + var result = await GetResponseAsStringAsync("/Authorization/AuthTestPage"); + result.ShouldBe("OK"); + } + } +} \ No newline at end of file From 65a7ea84a2173a1b900f4426d0b2dd799750d19e Mon Sep 17 00:00:00 2001 From: maliming Date: Mon, 30 Mar 2020 14:54:03 +0800 Subject: [PATCH 30/62] Removed UnitOfWork attribute in razor page model. --- .../src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs | 3 --- .../src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs | 1 - 2 files changed, 4 deletions(-) diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs index 014088983a..db0cf372d7 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs @@ -87,7 +87,6 @@ namespace Volo.Abp.Account.Web.Pages.Account return Page(); } - [UnitOfWork] //TODO: Will be removed when we implement action filter public virtual async Task OnPostAsync(string action) { await CheckLocalLoginAsync(); @@ -140,7 +139,6 @@ namespace Volo.Abp.Account.Web.Pages.Account return RedirectSafely(ReturnUrl, ReturnUrlHash); } - [UnitOfWork] public virtual async Task OnPostExternalLogin(string provider) { var redirectUrl = Url.Page("./Login", pageHandler: "ExternalLoginCallback", values: new { ReturnUrl, ReturnUrlHash }); @@ -150,7 +148,6 @@ namespace Volo.Abp.Account.Web.Pages.Account return await Task.FromResult(Challenge(properties, provider)); } - [UnitOfWork] public virtual async Task OnGetExternalLoginCallbackAsync(string returnUrl = "", string returnUrlHash = "", string remoteError = null) { //TODO: Did not implemented Identity Server 4 sample for this method (see ExternalLoginCallback in Quickstart of IDS4 sample) diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs index 0cb71f2ed9..db085d6587 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Register.cshtml.cs @@ -36,7 +36,6 @@ namespace Volo.Abp.Account.Web.Pages.Account await CheckSelfRegistrationAsync(); } - [UnitOfWork] //TODO: Will be removed when we implement action filter public virtual async Task OnPostAsync() { ValidateModel(); From 8df24a62fd35acd64b7f1f79a8c30f74c6d20805 Mon Sep 17 00:00:00 2001 From: Oliver Cooper Date: Thu, 2 Apr 2020 17:44:17 +0100 Subject: [PATCH 31/62] Add Quartz StartupDelay option. --- .../Volo/Abp/Quartz/AbpQuartzModule.cs | 9 ++++++++- .../Volo/Abp/Quartz/AbpQuartzPreOptions.cs | 11 ++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs index c16ab14f4d..615009f914 100644 --- a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs +++ b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs @@ -1,4 +1,5 @@ using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; using Quartz; using Quartz.Impl; using Quartz.Spi; @@ -20,9 +21,15 @@ namespace Volo.Abp.Quartz public override void OnApplicationInitialization(ApplicationInitializationContext context) { + var options = context.ServiceProvider.GetRequiredService>().Value; + _scheduler = context.ServiceProvider.GetService(); _scheduler.JobFactory = context.ServiceProvider.GetService(); - _scheduler.Start(); + + if (options.StartDelay != null && options.StartDelay.Ticks > 0) + _scheduler.StartDelayed(options.StartDelay); + else + _scheduler.Start(); } public override void OnApplicationShutdown(ApplicationShutdownContext context) diff --git a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzPreOptions.cs b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzPreOptions.cs index e86f993d1d..9e48744d96 100644 --- a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzPreOptions.cs +++ b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzPreOptions.cs @@ -1,14 +1,23 @@ -using System.Collections.Specialized; +using System; +using System.Collections.Specialized; namespace Volo.Abp.Quartz { public class AbpQuartzPreOptions { + /// + /// The quartz configuration. Available properties can be found within Quartz.Impl.StdSchedulerFactory. + /// public NameValueCollection Properties { get; set; } + /// + /// How long Quartz should wait before starting. Default: 0. + /// + public TimeSpan StartDelay { get; set; } public AbpQuartzPreOptions() { Properties = new NameValueCollection(); + StartDelay = new TimeSpan(0); } } } From b0f954036be466f40c35166ab321118429d24826 Mon Sep 17 00:00:00 2001 From: Oliver Cooper Date: Fri, 3 Apr 2020 07:30:40 +0000 Subject: [PATCH 32/62] Added braces and removed null check on StartDelay. --- .../src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs index 615009f914..ccd0327ecf 100644 --- a/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs +++ b/framework/src/Volo.Abp.Quartz/Volo/Abp/Quartz/AbpQuartzModule.cs @@ -26,10 +26,14 @@ namespace Volo.Abp.Quartz _scheduler = context.ServiceProvider.GetService(); _scheduler.JobFactory = context.ServiceProvider.GetService(); - if (options.StartDelay != null && options.StartDelay.Ticks > 0) + if (options.StartDelay.Ticks > 0) + { _scheduler.StartDelayed(options.StartDelay); + } else + { _scheduler.Start(); + } } public override void OnApplicationShutdown(ApplicationShutdownContext context) From fb8dee5cccff615c8f92b8734a292721986294ea Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 7 Apr 2020 11:13:42 +0800 Subject: [PATCH 33/62] Enhance EntityHelper's TrySetId method. Resolve #3461 --- .../Volo/Abp/Domain/Entities/EntityHelper.cs | 46 +++++++++++-------- .../Abp/Domain/Entities/EntityHelper_Tests.cs | 19 ++++++++ 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs index 2b38059ef8..5233f2accc 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Linq.Expressions; using System.Reflection; using JetBrains.Annotations; @@ -95,32 +97,40 @@ namespace Volo.Abp.Domain.Entities var lambdaBody = Expression.Equal(leftExpression, rightExpression); return Expression.Lambda>(lambdaBody, lambdaParam); } - + + private static readonly ConcurrentDictionary CachedPropertyInfo = + new ConcurrentDictionary(); + public static void TrySetId( IEntity entity, Func idFactory, bool checkForDisableGuidGenerationAttribute = false) { - //TODO: Can be optimized (by caching per entity type)? - var entityType = entity.GetType(); - var idProperty = entityType.GetProperty( - nameof(entity.Id) - ); + var property = CachedPropertyInfo.GetOrAdd( + entity.GetType().FullName + checkForDisableGuidGenerationAttribute, () => + { + var entityType = entity.GetType(); + var idProperty = entityType.GetProperties() + .Where(x => x.GetSetMethod(true) != null) + .FirstOrDefault(x => x.Name == nameof(entity.Id)); - if (idProperty == null || idProperty.GetSetMethod(true) == null) - { - return; - } + if (idProperty == null) + { + return null; + } - if (checkForDisableGuidGenerationAttribute) - { - if (idProperty.IsDefined(typeof(DisableIdGenerationAttribute), true)) - { - return; - } - } + if (checkForDisableGuidGenerationAttribute) + { + if (idProperty.IsDefined(typeof(DisableIdGenerationAttribute), true)) + { + return null; + } + } + + return idProperty; + }); - idProperty.SetValue(entity, idFactory()); + property?.SetValue(entity, idFactory()); } } } diff --git a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/EntityHelper_Tests.cs b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/EntityHelper_Tests.cs index 2ad6a45b05..8d405f6907 100644 --- a/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/EntityHelper_Tests.cs +++ b/framework/test/Volo.Abp.Ddd.Tests/Volo/Abp/Domain/Entities/EntityHelper_Tests.cs @@ -1,4 +1,5 @@ using System; +using System.ComponentModel; using Shouldly; using Xunit; @@ -33,6 +34,15 @@ namespace Volo.Abp.Domain.Entities myEntityDisablesIdGeneration.Id.ShouldBe(default); } + [Fact] + public static void SetId_NewIdFromBaseClass() + { + var idValue = Guid.NewGuid(); + var myNewIdEntity = new NewIdEntity(); + EntityHelper.TrySetId(myNewIdEntity, () => idValue, true); + myNewIdEntity.Id.ShouldBe(idValue); + } + private class MyEntityDerivedFromAggregateRoot : AggregateRoot { @@ -53,5 +63,14 @@ namespace Volo.Abp.Domain.Entities [DisableIdGeneration] public override Guid Id { get; protected set; } } + + public class NewIdEntity : Entity + { + public new Guid Id + { + get => base.Id; + set => base.Id = value; + } + } } } From c1e969cc3cdd619c436d6bfa281a6139451d2d00 Mon Sep 17 00:00:00 2001 From: Weihan Li Date: Tue, 7 Apr 2020 11:38:36 +0800 Subject: [PATCH 34/62] update IsRelational extension for EntityFramework --- .../Volo/Abp/EntityFrameworkCore/DatabaseFacadeExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DatabaseFacadeExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DatabaseFacadeExtensions.cs index f663f43a0d..fbea680be1 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DatabaseFacadeExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DatabaseFacadeExtensions.cs @@ -8,7 +8,9 @@ namespace Volo.Abp.EntityFrameworkCore { public static bool IsRelational(this DatabaseFacade database) { - return database.GetInfrastructure().GetService() != null; +#pragma warning disable EF1001 // Internal EF Core API usage. + return ((IDatabaseFacadeDependenciesAccessor)database).Dependencies is IRelationalDatabaseFacadeDependencies; +#pragma warning restore EF1001 // Internal EF Core API usage. } } } From 6e6c884eaf5ad437042cd131f40afadbd94a181e Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 9 Apr 2020 09:11:55 +0800 Subject: [PATCH 35/62] Remove Microsoft.AspNetCore.Mvc.DataAnnotations package. Resolve #3511 --- .../MyCompanyName.MyProjectName.Domain.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj index 2078a20b56..5f7dc8c7b0 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Domain/MyCompanyName.MyProjectName.Domain.csproj @@ -12,7 +12,6 @@ - From c3f38382d0d5bc4b95cec63a2398a04cd0bfc082 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 9 Apr 2020 13:34:40 +0800 Subject: [PATCH 36/62] Introducing AbpRemoteServiceApiDescriptionProvider Resolve #3458 --- .../AspNetCore/Mvc/AbpAspNetCoreMvcModule.cs | 23 +++++ .../AbpRemoteServiceApiDescriptionProvider.cs | 92 +++++++++++++++++++ ...oteServiceApiDescriptionProviderOptions.cs | 10 ++ 3 files changed, 125 insertions(+) create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs create mode 100644 framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProviderOptions.cs 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 21a675ddc4..9a6a224c48 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 @@ -11,15 +11,19 @@ using Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation; using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Reflection; +using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.Extensions.Hosting; using Volo.Abp.ApiVersioning; +using Volo.Abp.AspNetCore.Mvc.ApiExploring; using Volo.Abp.AspNetCore.Mvc.Conventions; using Volo.Abp.AspNetCore.Mvc.DependencyInjection; using Volo.Abp.AspNetCore.Mvc.Json; using Volo.Abp.AspNetCore.Mvc.Localization; using Volo.Abp.AspNetCore.VirtualFileSystem; using Volo.Abp.DependencyInjection; +using Volo.Abp.Http; using Volo.Abp.Http.Modeling; using Volo.Abp.Localization; using Volo.Abp.Modularity; @@ -50,6 +54,25 @@ namespace Volo.Abp.AspNetCore.Mvc options.IgnoredInterfaces.AddIfNotContains(typeof(IActionFilter)); }); + Configure(options => + { + var statusCodes = new List + { + (int) HttpStatusCode.Forbidden, + (int) HttpStatusCode.Unauthorized, + (int) HttpStatusCode.BadRequest, + (int) HttpStatusCode.NotFound, + (int) HttpStatusCode.NotImplemented, + (int) HttpStatusCode.InternalServerError + }; + + options.SupportedResponseTypes.AddIfNotContains(statusCodes.Select(statusCode => new ApiResponseType + { + Type = typeof(RemoteServiceErrorResponse), + StatusCode = statusCode + })); + }); + context.Services.PostConfigure(options => { if (options.MinifyGeneratedScript == null) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs new file mode 100644 index 0000000000..1714d74e65 --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Mvc.ApiExplorer; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Formatters; +using Microsoft.AspNetCore.Mvc.ModelBinding; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Reflection; + +namespace Volo.Abp.AspNetCore.Mvc.ApiExploring +{ + public class AbpRemoteServiceApiDescriptionProvider : IApiDescriptionProvider, ITransientDependency + { + private readonly IModelMetadataProvider _modelMetadataProvider; + private readonly MvcOptions _mvcOptions; + private readonly AbpRemoteServiceApiDescriptionProviderOptions _options; + + public AbpRemoteServiceApiDescriptionProvider( + IModelMetadataProvider modelMetadataProvider, + IOptions mvcOptionsAccessor, + IOptions optionsAccessor) + { + _modelMetadataProvider = modelMetadataProvider; + _mvcOptions = mvcOptionsAccessor.Value; + _options = optionsAccessor.Value; + } + + public void OnProvidersExecuted(ApiDescriptionProviderContext context) + { + } + + /// + /// The order -999 ensures that this provider is executed right after the + /// Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider. + /// + public int Order => -999; + + public void OnProvidersExecuting(ApiDescriptionProviderContext context) + { + foreach (var apiResponseType in GetApiResponseTypes()) + { + foreach (var result in context.Results.Where(x => ShouldAddResponseTypes(x.ActionDescriptor))) + { + result.SupportedResponseTypes.AddIfNotContains(x => x.StatusCode == apiResponseType.StatusCode, () => apiResponseType); + } + } + } + + protected virtual IEnumerable GetApiResponseTypes() + { + foreach (var apiResponse in _options.SupportedResponseTypes) + { + apiResponse.ModelMetadata = _modelMetadataProvider.GetMetadataForType(apiResponse.Type); + + foreach (var responseTypeMetadataProvider in _mvcOptions.OutputFormatters.OfType()) + { + var formatterSupportedContentTypes = responseTypeMetadataProvider.GetSupportedContentTypes(null, apiResponse.Type); + if (formatterSupportedContentTypes == null) + { + continue; + } + + foreach (var formatterSupportedContentType in formatterSupportedContentTypes) + { + apiResponse.ApiResponseFormats.Add(new ApiResponseFormat + { + Formatter = (IOutputFormatter) responseTypeMetadataProvider, + MediaType = formatterSupportedContentType + }); + } + } + } + + return _options.SupportedResponseTypes; + } + + protected virtual bool ShouldAddResponseTypes(ActionDescriptor actionDescriptor) + { + if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo()) != null || + ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo()) != null) + { + return false; + } + + var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo()); + return remoteServiceAttr != null && remoteServiceAttr.IsEnabled; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProviderOptions.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProviderOptions.cs new file mode 100644 index 0000000000..74e631c53c --- /dev/null +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProviderOptions.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc.ApiExplorer; + +namespace Volo.Abp.AspNetCore.Mvc.ApiExploring +{ + public class AbpRemoteServiceApiDescriptionProviderOptions + { + public HashSet SupportedResponseTypes { get; set; } = new HashSet(); + } +} \ No newline at end of file From efaf7fe3e05063fd1568211a412c16f3f79baf45 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Fri, 10 Apr 2020 21:55:42 +0800 Subject: [PATCH 37/62] Update document --- ...-Application-Modules-Extending-Entities.md | 2 - ...Application-Modules-Overriding-Services.md | 99 +++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md b/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md index 4cc73989fc..76a43ea80a 100644 --- a/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md +++ b/docs/zh-Hans/Customizing-Application-Modules-Extending-Entities.md @@ -58,8 +58,6 @@ ObjectExtensionManager.Instance ## 创建新实体映射到同一个数据库表/Collection -尽管额外属性方法**易于使用**并且适用于一些场景,但它具有[实体文档](Entities.md)中描述的一些缺点. - 另一个方法是**创建你自己的实体**映射到**同一个数据库库**(对于MongoDB数据库是collection) [应用程序启动模板](Startup-Templates/Application.md)的 `AppUser` 已经实现了这种方法. [EF Core迁移文档](Entity-Framework-Core-Migrations.md)描述了在这些情况下如何实现和管理**EF Core数据库迁移**. 这种方法同样适用于MongoDB,但你不需要处理数据库迁移问题. diff --git a/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md b/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md index cb40de9bdb..4b98f7cfb2 100644 --- a/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md +++ b/docs/zh-Hans/Customizing-Application-Modules-Overriding-Services.md @@ -161,6 +161,105 @@ public class MyIdentityUserManager : IdentityUserManager 控制器,框架服务,视图组件类以及其他类型注册到依赖注入的类都可以像上面的示例那样被重写. +## 扩展数据传输对象 + +你可以如[扩展实体文档](Customizing-Application-Modules-Extending-Entities.md)所述扩展实体. 并使用上面介绍的重写相关服务**使用自定义属性****执行其他业务逻辑**. + +应用程序使用的数据传输对象(**DTO**)同样可扩展. 这样你可以使服务返回其他属性并在UI(或其他客户端)得到其他属性. + +### 示例 + +假设你已经按照[扩展实体文档](Customizing-Application-Modules-Extending-Entities.md)中的说明添加了 `SocialSecurityNumber` 并希望从 `IdentityUserAppService的GetListAsync` 方法获取用户列表时包括此属性. + +你可以使用[对象扩展系统](Object-Extensions.md)将属性添加到 `IdentityUserDto`. 在应用程序启动模板带有的 `YourProjectNameDtoExtensions` 类中编写以下代码: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "SocialSecurityNumber" + ); +```` + +这段代码为 `IdentityUserDto` 类添加了 `string` 类型的 `SocialSecurityNumber` 属性. 现在你可以在RREST API客户端调用 `/api/identity/users` HTTP API(内部使用 `IdentityUserAppService`),你会在 `extraProperties` 部分看到 `SocialSecurityNumber` 值. + +````json +{ + "totalCount": 1, + "items": [{ + "tenantId": null, + "userName": "admin", + "name": "admin", + "surname": null, + "email": "admin@abp.io", + "emailConfirmed": false, + "phoneNumber": null, + "phoneNumberConfirmed": false, + "twoFactorEnabled": false, + "lockoutEnabled": true, + "lockoutEnd": null, + "concurrencyStamp": "b4c371a0ab604de28af472fa79c3b70c", + "isDeleted": false, + "deleterId": null, + "deletionTime": null, + "lastModificationTime": "2020-04-09T21:25:47.0740706", + "lastModifierId": null, + "creationTime": "2020-04-09T21:25:46.8308744", + "creatorId": null, + "id": "8edecb8f-1894-a9b1-833b-39f4725db2a3", + "extraProperties": { + "SocialSecurityNumber": "123456789" + } + }] +} +```` + +手动添加了 `123456789` 值到数据库中. + +所有预构建的模块都在DTO中支持额外属性,你可以对其轻松的配置. + +### 定义检查 + +当为实体[定义](Customizing-Application-Modules-Extending-Entities.md)额外的属性时,由于安全性它不会自动出现在所有相关的DTO中. 额外属性可能包含敏感数据并且你可能不想默认公开给客户端. + +因此如果要用于DTO,需要为相应的DTO显式定义相同的属性(如上所述). 如果要允许在用户创建时进行设置还需要为 `IdentityUserCreateDto` 定义. + +如果属性并不是安全敏感,这可能会很枯燥. 对象扩展系统允许你忽略检查定义的属性. 参阅示例: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "SocialSecurityNumber", + options => + { + options.MapEfCore(b => b.HasMaxLength(32)); + options.CheckPairDefinitionOnMapping = false; + } + ); +```` + +这是定义实体属性的另一种方法( 有关 `ObjectExtensionManager` 更多信息,请参阅[文档](Object-Extensions.md)). 这次我们设置了 `CheckPairDefinitionOnMapping` 为false,在将实体映射到DTO时会跳过定义检查. + +如果你不喜欢这种方法,但想简单的向多个对象(DTO)添加单个属, `AddOrUpdateProperty` 可以使用类型数组添加额外的属性: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + new[] + { + typeof(IdentityUserDto), + typeof(IdentityUserCreateDto), + typeof(IdentityUserUpdateDto) + }, + "SocialSecurityNumber" + ); +```` + +### 关于用户界面 + +该系统允许你向实体和DTO添加额外的属性并执行自定义业务代码,但它与用户界面无关. + +参阅 [重写用户界面](Customizing-Application-Modules-Overriding-User-Interface.md) 指南了解关于UI部分. + ## 如何找到服务? [模块文档](Modules/Index.md) 包含了定义的主要服务列表. 另外 你也可以查看[源码](https://github.com/abpframework/abp/tree/dev/modules)找到所有的服务. \ No newline at end of file From 66595e35f55fb1d79e690a2ac8e6b5634c8b05d6 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Fri, 10 Apr 2020 22:57:10 +0800 Subject: [PATCH 38/62] Translate object extensions document --- docs/zh-Hans/AutoMapper-Integration.md | 3 - .../Best-Practices/Application-Services.md | 18 +- .../Best-Practices/Data-Transfer-Objects.md | 1 + docs/zh-Hans/Object-Extensions.md | 267 ++++++++++++++++++ docs/zh-Hans/Object-To-Object-Mapping.md | 17 ++ docs/zh-Hans/docs-nav.json | 4 + 6 files changed, 301 insertions(+), 9 deletions(-) delete mode 100644 docs/zh-Hans/AutoMapper-Integration.md create mode 100644 docs/zh-Hans/Object-Extensions.md diff --git a/docs/zh-Hans/AutoMapper-Integration.md b/docs/zh-Hans/AutoMapper-Integration.md deleted file mode 100644 index d197861f25..0000000000 --- a/docs/zh-Hans/AutoMapper-Integration.md +++ /dev/null @@ -1,3 +0,0 @@ -## AutoMapper Integration - -TODO \ No newline at end of file diff --git a/docs/zh-Hans/Best-Practices/Application-Services.md b/docs/zh-Hans/Best-Practices/Application-Services.md index 6c8d11b32b..6c681edde1 100644 --- a/docs/zh-Hans/Best-Practices/Application-Services.md +++ b/docs/zh-Hans/Best-Practices/Application-Services.md @@ -17,7 +17,7 @@ ##### 基础DTO -**推荐** 为实体定义一个**基础**DTO. +**推荐** 为聚合根定义一个**基础**DTO. - 直接包含实体中所有的**原始属性**. - 例外: 出于**安全**原因,可以**排除**某些属性(像 `User.Password`). @@ -27,7 +27,7 @@ ```c# [Serializable] -public class IssueDto : FullAuditedEntityDto +public class IssueDto : ExtensibleFullAuditedEntityDto { public string Title { get; set; } public string Text { get; set; } @@ -57,7 +57,7 @@ public class IssueLabelDto ````C# [Serializable] -public class IssueWithDetailsDto : FullAuditedEntityDto +public class IssueWithDetailsDto : ExtensibleFullAuditedEntityDto { public string Title { get; set; } public string Text { get; set; } @@ -66,14 +66,14 @@ public class IssueWithDetailsDto : FullAuditedEntityDto } [Serializable] -public class MilestoneDto : EntityDto +public class MilestoneDto : ExtensibleEntityDto { public string Name { get; set; } public bool IsClosed { get; set; } } [Serializable] -public class LabelDto : EntityDto +public class LabelDto : ExtensibleEntityDto { public string Name { get; set; } public string Color { get; set; } @@ -120,6 +120,7 @@ Task> GetListAsync(QuestionListQueryDto queryDto); * **推荐** 使用 `CreateAsync` 做为**方法名**. * **推荐** 使用**专门的输入DTO**来创建实体. +* **推荐** DTO类从 `ExtensibleObject` 类继承(或任何实现 `ExtensibleObject`的类) 以允许在需要时传递额外的属性. * **推荐** 使用 **data annotations** 进行输入验证. * 尽可能在**领域**之间共享常量(通过**domain shared** package定义的常量). * **推荐** 只需要创建实体的**最少**信息, 但是提供了其他可选属性. @@ -134,7 +135,7 @@ Task CreateAsync(CreateQuestionDto questionDto); ````C# [Serializable] -public class CreateQuestionDto +public class CreateQuestionDto : ExtensibleObject { [Required] [StringLength(QuestionConsts.MaxTitleLength, MinimumLength = QuestionConsts.MinTitleLength)] @@ -151,6 +152,7 @@ public class CreateQuestionDto - **推荐** 使用 `UpdateAsync` 做为**方法名**. - **推荐** 使用**专门的输入DTO**来更新实体. +- **推荐** DTO类从 `ExtensibleObject` 类继承(或任何实现 `ExtensibleObject`的类) 以允许在需要时传递额外的属性. - **推荐** 获取实体的id做为分离的原始参数. 不要包含更新DTO. - **推荐** 使用 **data annotations** 进行输入验证. - 尽可能在**领域**之间共享常量(通过**domain shared** package定义的常量). @@ -199,6 +201,10 @@ Task VoteAsync(Guid id, VoteType type); * **不推荐** 在应用程序服务方法中使用linq/sql查询来自数据库的数据. 让仓储负责从数据源执行linq/sql查询. +#### 额外的属性 + +* **推荐** 使用 `MapExtraPropertiesTo` 扩展方法 ([参阅](Object-Extensions.md)) 或配置对象映射 (`MapExtraProperties`) 以允许应用开发人员能够扩展对象和服务. + #### 操作/删除 实体 * **推荐** 总是从数据库中获取所有的相关实体以对他们执行操作. diff --git a/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md b/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md index 0c6017e6e7..29e51501ea 100644 --- a/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md +++ b/docs/zh-Hans/Best-Practices/Data-Transfer-Objects.md @@ -2,6 +2,7 @@ * **推荐** 在 **application.contracts** 层中定义DTO. * **推荐** 在可能和必要的情况下从预构建的 **基础DTO类** 继承 (如 `EntityDto`, `CreationAuditedEntityDto`, `AuditedEntityDto`, `FullAuditedEntityDto` 等). +* **推荐** 从**聚合根**的**扩展DTO**继承(如 `ExtensibleAuditedEntityDto`), 因为聚合根是可扩展的额外的属性使用这种方式映射到DTO. * **推荐** 定义 **public getter 和 setter** 的DTO成员 . * **推荐** 使用 **data annotations** **验证** service输入DTO的属性. * **不推荐** 在DTO中添加任何 **逻辑**, 在必要的时候可以实现 `IValidatableObject` 接口. diff --git a/docs/zh-Hans/Object-Extensions.md b/docs/zh-Hans/Object-Extensions.md new file mode 100644 index 0000000000..27957708f4 --- /dev/null +++ b/docs/zh-Hans/Object-Extensions.md @@ -0,0 +1,267 @@ +# 对象扩展 + +ABP框架提供了 **实体扩展系统** 允许你 **添加额外属性** 到已存在的对象 **无需修改相关类**. 它允许你扩展[应用程序依赖模块](Modules/Index.md)实现的功能,尤其是当你要扩展[模块定义的实体](Customizing-Application-Modules-Extending-Entities.md)和[DTO](Customizing-Application-Modules-Overriding-Services.md)时. + +> 你自己的对象通常不需要对象扩展系统,因为你可以轻松的添加常规属性到你的类中. + +## IHasExtraProperties 接口 + +这是一个使类可扩展的接口. 它定义了 `Dictionary` 属性: + +````csharp +Dictionary ExtraProperties { get; } +```` + +然后你可以使用此字典添加或获取其他属性. + +### 基类 + +默认以下基类实现了 `IHasExtraProperties` 接口: + +* 由 `AggregateRoot` 类实现 (参阅 [entities](Entities.md)). +* 由 `ExtensibleEntityDto`, `ExtensibleAuditedEntityDto`... [DTO](Data-Transfer-Objects.md)基类实现. +* 由 `ExtensibleObject` 实现, 它是一个简单的基类,任何类型的对象都可以继承. + +如果你的类从这些类继承,那么你的类也是可扩展的,如果没有,你也可以随时手动继承. + +### 基本扩展方法 + +虽然可以直接使用类的 `ExtraProperties` 属性,但建议使用以下扩展方法使用额外属性. + +#### SetProperty + +用于设置额外属性值: + +````csharp +user.SetProperty("Title", "My Title"); +user.SetProperty("IsSuperUser", true); +```` + +`SetProperty` 返回相同的对象, 你可以使用链式编程: + +````csharp +user.SetProperty("Title", "My Title") + .SetProperty("IsSuperUser", true); +```` + +#### GetProperty + +用于读取额外属性的值: + +````csharp +var title = user.GetProperty("Title"); + +if (user.GetProperty("IsSuperUser")) +{ + //... +} +```` + +* `GetProperty` 是一个泛型方法,对象类型做为泛型参数. +* 如果未设置给定的属性,则返回默认值 (`int` 的默认值为 `0` , `bool` 的默认值是 `false` ... 等). + +##### 非基本属性类型 + +如果您的属性类型不是原始类型(int,bool,枚举,字符串等),你需要使用 `GetProperty` 的非泛型版本,它会返回 `object`. + +#### HasProperty + +用于检查对象之前是否设置了属性. + +#### RemoveProperty + +用于从对象中删除属性. 使用此方法代替为属性设置 `null` 值. + +### 一些最佳实践 + +为属性名称使用魔术字符串很危险,因为你很容易输入错误的属性名称-这并不安全; + +* 为你的额外属性名称定义一个常量. +* 使用扩展方法轻松设置你的属性. + +示例: + +````csharp +public static class IdentityUserExtensions +{ + private const string TitlePropertyName = "Title"; + + public static void SetTitle(this IdentityUser user, string title) + { + user.SetProperty(TitlePropertyName, title); + } + + public static string GetTitle(this IdentityUser user) + { + return user.GetProperty(TitlePropertyName); + } +} +```` + +然后, 你可以很容易地设置或获取 `Title` 属性: + +````csharp +user.SetTitle("My Title"); +var title = user.GetTitle(); +```` + +## Object Extension Manager + +你可以为可扩展对象(实现 `IHasExtraProperties`接口)设置任意属性, `ObjectExtensionManager` 用于显式定义可扩展类的其他属性. + +显式定义额外的属性有一些用例: + +* 允许控制如何在对象到对象的映射上处理额外的属性 (参阅下面的部分). +* 允许定义属性的元数据. 例如你可以在使用[EF Core](Entity-Framework-Core.md)时将额外的属性映射到数据库中的表字段. + +> `ObjectExtensionManager` 实现单例模式 (`ObjectExtensionManager.Instance`) ,你应该在应用程序启动之前定义对象扩展. [应用程序启动模板](Startup-Templates/Application.md) 有一些预定义的静态类,可以安全在内部定义对象扩展. + +### AddOrUpdate + +`AddOrUpdate` 是定义对象额外属性或更新对象额外属性的主要方法. + +示例: 为 `IdentityUser` 实体定义额外属性: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdate(options => + { + options.AddOrUpdateProperty("SocialSecurityNumber"); + options.AddOrUpdateProperty("IsSuperUser"); + } + ); +```` + +### AddOrUpdateProperty + +虽然可以如上所示使用 `AddOrUpdateProperty`, 但如果要定义单个额外的属性,也可以使用快捷的扩展方法: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty("SocialSecurityNumber"); +```` + +有时将单个额外属性定义为多种类型是可行的. 你可以使用以下代码,而不是一个一个地定义: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + new[] + { + typeof(IdentityUserDto), + typeof(IdentityUserCreateDto), + typeof(IdentityUserUpdateDto) + }, + "SocialSecurityNumber" + ); +```` + +#### 属性配置 + +`AddOrUpdateProperty` 还可以为属性定义执行其他配置的操作. + +Example: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "SocialSecurityNumber", + options => + { + options.CheckPairDefinitionOnMapping = false; + }); +```` + +> 参阅 "对象到对象映射" 部分了解 `CheckPairDefinitionOnMapping` 选项. + +`options` 有一个名为 `Configuration` 的字典,该字典存储对象扩展定义甚至可以扩展. EF Core使用它来将其他属性映射到数据库中的表字段. 请参阅[扩展实体文档](Customizing-Application-Modules-Extending-Entities.md). + +## 对象到对象映射 + +假设你已向可扩展的实体对象添加了额外的属性并使用了自动[对象到对象的映射](Object-To-Object-Mapping.md)将该实体映射到可扩展的DTO类. 在这种情况下你需要格外小心,因为额外属性可能包含**敏感数据**,这些数据对于客户端不可用. + +本节提供了一些**好的做法**,可以控制对象映射的额外属性。 + +### MapExtraPropertiesTo + +`MapExtraPropertiesTo` 是ABP框架提供的扩展方法,用于以受控方式将额外的属性从一个对象复制到另一个对象. 示例: + +````csharp +identityUser.MapExtraPropertiesTo(identityUserDto); +```` + +`MapExtraPropertiesTo` 需要在**两侧**(本例中是`IdentityUser` 和 `IdentityUserDto`)**定义属性**. 以将值复制到目标对象. 否则即使源对象(在此示例中为 `identityUser` )中确实存在该值,它也不会复制. 有一些重载此限制的方法. + +#### MappingPropertyDefinitionChecks + +`MapExtraPropertiesTo` 获取一个附加参数来控制单个映射操作的定义检查: + +````csharp +identityUser.MapExtraPropertiesTo( + identityUserDto, + MappingPropertyDefinitionChecks.None +); +```` + +> 要小心,因为 `MappingPropertyDefinitionChecks.None` 会复制所有的额外属性而不进行任何检查. `MappingPropertyDefinitionChecks` 枚举还有其他成员. + +如果要完全禁用属性的定义检查,可以在定义额外的属性(或更新现有定义)时进行,如下所示: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "SocialSecurityNumber", + options => + { + options.CheckPairDefinitionOnMapping = false; + }); +```` + +#### 忽略属性 + +你可能要在映射操作忽略某些属性: + +````csharp +identityUser.MapExtraPropertiesTo( + identityUserDto, + ignoredProperties: new[] {"MySensitiveProp"} +); +```` + +忽略的属性不会复制到目标对象. + +#### AutoMapper集成 + +如果您使用的是[AutoMapper](https://automapper.org/)库,ABP框架还提供了一种扩展方法来利用上面定义的 `MapExtraPropertiesTo` 方法. + +你可以在映射配置文件中使用 `MapExtraProperties()` 方法. + +````csharp +public class MyProfile : Profile +{ + public MyProfile() + { + CreateMap() + .MapExtraProperties(); + } +} +```` + +它与 `MapExtraPropertiesTo()` 方法具有相同的参数。 + +## Entity Framework Core 数据库映射 + +如果你使用的是EF Core,可以将额外的属性映射到数据库中的表字段. 例: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "SocialSecurityNumber", + options => + { + options.MapEfCore(b => b.HasMaxLength(32)); + } + ); +```` + +参阅 [Entity Framework Core 集成文档](Entity-Framework-Core.md) 了解更多内容. \ No newline at end of file diff --git a/docs/zh-Hans/Object-To-Object-Mapping.md b/docs/zh-Hans/Object-To-Object-Mapping.md index 67d580ae54..57b744e0bf 100644 --- a/docs/zh-Hans/Object-To-Object-Mapping.md +++ b/docs/zh-Hans/Object-To-Object-Mapping.md @@ -145,6 +145,23 @@ options.AddProfile(validate: true); > 如果你有多个配置文件,并且只需要为其中几个启用验证,那么首先使用`AddMaps`而不进行验证,然后为你想要验证的每个配置文件使用`AddProfile`. +### 映射对象扩展 + +[对象扩展系统](Object-Extensions.md) 允许为已存在的类定义额外属性. ABP 框架提供了一个映射定义扩展可以正确的映射两个对象的额外属性. + +````csharp +public class MyProfile : Profile +{ + public MyProfile() + { + CreateMap() + .MapExtraProperties(); + } +} +```` + +如果两个类都是可扩展对象(实现了 `IHasExtraProperties` 接口),建议使用 `MapExtraProperties` 方法. 更多信息请参阅[对象扩展文档](Object-Extensions.md). + ## 高级主题 ### IObjectMapper 接口 diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index f01ce04bc9..893e1c3236 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -140,6 +140,10 @@ { "text": "设置管理", "path": "Settings.md" + }, + { + "text": "对象扩展", + "path": "Object-Extensions.md" } ] }, From 43dedc8e7a604cd31a0610dfaee907180b069350 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Fri, 10 Apr 2020 22:59:56 +0800 Subject: [PATCH 39/62] Add data-filtering document nav --- docs/zh-Hans/docs-nav.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index 893e1c3236..59b91f8e4f 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -141,6 +141,10 @@ "text": "设置管理", "path": "Settings.md" }, + { + "text": "数据过滤", + "path": "Data-Filtering.md" + }, { "text": "对象扩展", "path": "Object-Extensions.md" From 85f1fc5496144dea0f58f1cc3f450e08f633f6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Fri, 10 Apr 2020 22:21:28 +0300 Subject: [PATCH 40/62] Create ObjectExtensionPropertyInfoValidationExtensions. --- ...tensionPropertyInfoValidationExtensions.cs | 68 +++++++++++++++++++ ...tExtensionPropertyInfoValidationOptions.cs | 25 +++++++ .../Abp/Validation/AbpValidationModule.cs | 1 + 3 files changed, 94 insertions(+) create mode 100644 framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationExtensions.cs create mode 100644 framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationOptions.cs diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationExtensions.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationExtensions.cs new file mode 100644 index 0000000000..926e086d21 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationExtensions.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; + +namespace Volo.Abp.ObjectExtending +{ + public static class ObjectExtensionPropertyInfoValidationExtensions + { + public const string ValidationConfigurationName = "Validation"; + + [NotNull] + public static ObjectExtensionPropertyInfo ConfigureValidation( + [NotNull] this ObjectExtensionPropertyInfo propertyExtension, + [NotNull] Action configureAction) + { + Check.NotNull(propertyExtension, nameof(propertyExtension)); + Check.NotNull(configureAction, nameof(configureAction)); + + configureAction(propertyExtension.GetOrAddValidationConfiguration()); + + return propertyExtension; + } + + [NotNull] + public static ObjectExtensionPropertyInfo AddValidationAttributes( + [NotNull] this ObjectExtensionPropertyInfo propertyExtension, + [NotNull] params ValidationAttribute[] validationAttributes) + { + Check.NotNull(propertyExtension, nameof(propertyExtension)); + Check.NotNullOrEmpty(validationAttributes, nameof(validationAttributes)); + + propertyExtension + .GetOrAddValidationConfiguration() + .ValidationAttributes + .AddRange(validationAttributes); + + return propertyExtension; + } + + [CanBeNull] + public static ObjectExtensionPropertyInfoValidationOptions GetValidationConfigurationOrNull( + [NotNull] this ObjectExtensionPropertyInfo propertyExtension) + { + Check.NotNull(propertyExtension, nameof(propertyExtension)); + + return propertyExtension.Configuration.GetOrDefault(ValidationConfigurationName) + as ObjectExtensionPropertyInfoValidationOptions; + } + + [NotNull] + public static ObjectExtensionPropertyInfoValidationOptions GetOrAddValidationConfiguration( + [NotNull] this ObjectExtensionPropertyInfo propertyExtension) + { + Check.NotNull(propertyExtension, nameof(propertyExtension)); + + var validationConfiguration = propertyExtension.GetValidationConfigurationOrNull(); + + if (validationConfiguration == null) + { + validationConfiguration = new ObjectExtensionPropertyInfoValidationOptions(propertyExtension); + propertyExtension.Configuration[ValidationConfigurationName] = validationConfiguration; + } + + return validationConfiguration; + } + } +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationOptions.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationOptions.cs new file mode 100644 index 0000000000..e28efc14df --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationOptions.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; + +namespace Volo.Abp.ObjectExtending +{ + public class ObjectExtensionPropertyInfoValidationOptions + { + [NotNull] + public ObjectExtensionPropertyInfo ExtensionProperty { get; } + + [NotNull] + public ObjectExtensionInfo ObjectExtension => ExtensionProperty.ObjectExtension; + + [NotNull] + public List ValidationAttributes { get; } + + public ObjectExtensionPropertyInfoValidationOptions( + [NotNull] ObjectExtensionPropertyInfo extensionProperty) + { + ExtensionProperty = Check.NotNull(extensionProperty, nameof(extensionProperty)); + ValidationAttributes = new List(); + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs index ad2be911de..9da6766a5d 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs @@ -18,6 +18,7 @@ namespace Volo.Abp.Validation context.Services.OnRegistred(ValidationInterceptorRegistrar.RegisterIfNeeded); AutoAddObjectValidationContributors(context.Services); } + public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => From a04805a6077fa3150424c4439edcb009c655b371 Mon Sep 17 00:00:00 2001 From: maliming Date: Sat, 11 Apr 2020 17:10:23 +0800 Subject: [PATCH 41/62] Filter ProducesResponseType. --- .../AbpRemoteServiceApiDescriptionProvider.cs | 22 ++++++++++--------- .../Volo/Abp/Reflection/ReflectionHelper.cs | 17 ++++++++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs index 1714d74e65..ae09573d60 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ApiExploring/AbpRemoteServiceApiDescriptionProvider.cs @@ -3,7 +3,6 @@ using System.Linq; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.ApiExplorer; -using Microsoft.AspNetCore.Mvc.Controllers; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.Extensions.Options; @@ -42,9 +41,18 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring { foreach (var apiResponseType in GetApiResponseTypes()) { - foreach (var result in context.Results.Where(x => ShouldAddResponseTypes(x.ActionDescriptor))) + foreach (var result in context.Results.Where(x => IsRemoteService(x.ActionDescriptor))) { - result.SupportedResponseTypes.AddIfNotContains(x => x.StatusCode == apiResponseType.StatusCode, () => apiResponseType); + var actionProducesResponseTypeAttributes = + ReflectionHelper.GetAttributesOfMemberOrDeclaringType( + result.ActionDescriptor.GetMethodInfo()); + if (actionProducesResponseTypeAttributes.Any(x => x.StatusCode == apiResponseType.StatusCode)) + { + continue; + } + + result.SupportedResponseTypes.AddIfNotContains(x => x.StatusCode == apiResponseType.StatusCode, + () => apiResponseType); } } } @@ -77,14 +85,8 @@ namespace Volo.Abp.AspNetCore.Mvc.ApiExploring return _options.SupportedResponseTypes; } - protected virtual bool ShouldAddResponseTypes(ActionDescriptor actionDescriptor) + protected virtual bool IsRemoteService(ActionDescriptor actionDescriptor) { - if (ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo()) != null || - ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo()) != null) - { - return false; - } - var remoteServiceAttr = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(actionDescriptor.GetMethodInfo()); return remoteServiceAttr != null && remoteServiceAttr.IsEnabled; } diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs index 8d6e6a42cf..fd47d8b859 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/Reflection/ReflectionHelper.cs @@ -108,6 +108,23 @@ namespace Volo.Abp.Reflection ?? defaultValue; } + /// + /// Tries to gets attributes defined for a class member and it's declaring type including inherited attributes. + /// + /// Type of the attribute + /// MemberInfo + /// Inherit attribute from base classes + public static IEnumerable GetAttributesOfMemberOrDeclaringType(MemberInfo memberInfo, bool inherit = true) + where TAttribute : class + { + var customAttributes = memberInfo.GetCustomAttributes(true).OfType(); + var declaringTypeCustomAttributes = + memberInfo.DeclaringType?.GetTypeInfo().GetCustomAttributes(true).OfType(); + return declaringTypeCustomAttributes != null + ? customAttributes.Concat(declaringTypeCustomAttributes).Distinct() + : customAttributes; + } + /// /// Gets value of a property by it's full path from given object /// From a18c190cdc4d71ae0d3b27c159862c874c4a2bc0 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sat, 11 Apr 2020 17:36:41 +0800 Subject: [PATCH 42/62] Make docs module shortName unique --- .../Docs/Admin/Projects/ProjectAdminAppService.cs | 10 ++++++++-- .../Volo/Docs/DocsDomainSharedModule.cs | 6 ++++++ .../Volo/Docs/Localization/Domain/en.json | 3 ++- .../Volo/Docs/Localization/Domain/zh-Hans.json | 3 ++- .../Volo/Docs/Localization/Domain/zh-Hant.json | 4 +++- .../Volo/Docs/Projects/IProjectRepository.cs | 2 ++ .../ProjectShortNameAlreadyExistsException.cs | 13 +++++++++++++ .../Volo/Docs/Projects/EfCoreProjectRepository.cs | 10 +++++++--- .../Volo/Docs/Projects/MongoProjectRepository.cs | 10 ++++++++-- 9 files changed, 51 insertions(+), 10 deletions(-) create mode 100644 modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/ProjectShortNameAlreadyExistsException.cs diff --git a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs index d5aef3e614..436c9d1c76 100644 --- a/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs +++ b/modules/docs/src/Volo.Docs.Admin.Application/Volo/Docs/Admin/Projects/ProjectAdminAppService.cs @@ -47,6 +47,11 @@ namespace Volo.Docs.Admin.Projects [Authorize(DocsAdminPermissions.Projects.Create)] public async Task CreateAsync(CreateProjectDto input) { + if (await _projectRepository.ShortNameExistsAsync(input.ShortName)) + { + throw new ProjectShortNameAlreadyExistsException(input.ShortName); + } + var project = new Project(_guidGenerator.Create(), input.Name, input.ShortName, @@ -64,9 +69,9 @@ namespace Volo.Docs.Admin.Projects foreach (var extraProperty in input.ExtraProperties) { - project.ExtraProperties.Add(extraProperty.Key,extraProperty.Value); + project.ExtraProperties.Add(extraProperty.Key, extraProperty.Value); } - + project = await _projectRepository.InsertAsync(project); return ObjectMapper.Map(project); @@ -101,5 +106,6 @@ namespace Volo.Docs.Admin.Projects { await _projectRepository.DeleteAsync(id); } + } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/DocsDomainSharedModule.cs b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/DocsDomainSharedModule.cs index 6c12059eff..d0df3af2fb 100644 --- a/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/DocsDomainSharedModule.cs +++ b/modules/docs/src/Volo.Docs.Domain.Shared/Volo/Docs/DocsDomainSharedModule.cs @@ -1,4 +1,5 @@ using Volo.Abp.Localization; +using Volo.Abp.Localization.ExceptionHandling; using Volo.Abp.Modularity; using Volo.Docs.Localization; @@ -13,6 +14,11 @@ namespace Volo.Docs { options.Resources.Add("en"); }); + + Configure(options => + { + options.MapCodeNamespace("Volo.Docs.Domain", typeof(DocsResource)); + }); } } } diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json index e8c7122ec3..6299a9aab2 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/en.json @@ -23,6 +23,7 @@ "New": "New", "Upd": "Upd", "NewExplanation": "Created in the last two weeks.", - "UpdatedExplanation": "Updated in the last two weeks." + "UpdatedExplanation": "Updated in the last two weeks.", + "Volo.Docs.Domain:010002": "ShortName {ShortName} already exists." } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json index 3d9df18295..ef4e95f3dc 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hans.json @@ -22,6 +22,7 @@ "New": "新文档", "Upd": "更新", "NewExplanation": "在最近两周内创建.", - "UpdatedExplanation": "在最近两周内更新." + "UpdatedExplanation": "在最近两周内更新.", + "Volo.Docs.Domain:010002": "简称 {ShortName} 已经存在." } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json index 9fbc677120..6a2f4d8799 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Localization/Domain/zh-Hant.json @@ -18,9 +18,11 @@ "DocumentNotFoundInSelectedLanguage": "本文件不適用於所選語系,將以預設語系顯示.", "FilterTopics": "過濾主題", "FullSearch": "搜索文件", + "Volo.Docs.Domain:010001": "Elastic search未啟用.", "New": "新文檔", "Upd": "更新", "NewExplanation": "在最近兩周內創建.", - "UpdatedExplanation": "在最近兩周內更新." + "UpdatedExplanation": "在最近兩周內更新.", + "Volo.Docs.Domain:010002": "簡稱 {ShortName} 已經存在." } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/IProjectRepository.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/IProjectRepository.cs index 4a6ad77ddd..add77749b4 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/IProjectRepository.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/IProjectRepository.cs @@ -10,5 +10,7 @@ namespace Volo.Docs.Projects Task> GetListAsync(string sorting, int maxResultCount, int skipCount); Task GetByShortNameAsync(string shortName); + + Task ShortNameExistsAsync(string shortName); } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/ProjectShortNameAlreadyExistsException.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/ProjectShortNameAlreadyExistsException.cs new file mode 100644 index 0000000000..b61972d60b --- /dev/null +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/ProjectShortNameAlreadyExistsException.cs @@ -0,0 +1,13 @@ +using Volo.Abp; + +namespace Volo.Docs.Projects +{ + public class ProjectShortNameAlreadyExistsException : BusinessException + { + public ProjectShortNameAlreadyExistsException(string shortName) + : base("Volo.Docs.Domain:010002") + { + WithData("ShortName", shortName); + } + } +} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs index b54869edcd..0611624029 100644 --- a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs +++ b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs @@ -13,10 +13,9 @@ namespace Volo.Docs.Projects { public class EfCoreProjectRepository : EfCoreRepository, IProjectRepository { - public EfCoreProjectRepository(IDbContextProvider dbContextProvider) + public EfCoreProjectRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider) { - } public async Task> GetListAsync(string sorting, int maxResultCount, int skipCount) @@ -39,5 +38,10 @@ namespace Volo.Docs.Projects return project; } + + public async Task ShortNameExistsAsync(string shortName) + { + return await DbSet.AnyAsync(x => x.ShortName == shortName); + } } -} +} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs index 6f2359e339..1dbb646405 100644 --- a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs +++ b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs @@ -14,7 +14,8 @@ namespace Volo.Docs.Projects { public class MongoProjectRepository : MongoDbRepository, IProjectRepository { - public MongoProjectRepository(IMongoDbContextProvider dbContextProvider) : base(dbContextProvider) + public MongoProjectRepository(IMongoDbContextProvider dbContextProvider) : base( + dbContextProvider) { } @@ -38,5 +39,10 @@ namespace Volo.Docs.Projects return project; } + + public async Task ShortNameExistsAsync(string shortName) + { + return await GetMongoQueryable().AnyAsync(x => x.ShortName == shortName); + } } -} +} \ No newline at end of file From 5e2cc4ea4746422a2e4c40334a02a5bbd5597531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 11 Apr 2020 12:48:47 +0300 Subject: [PATCH 43/62] Update DynamicProxyIgnoreTypes.cs --- .../Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs index 6cdefb6705..b788acbdad 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Volo.Abp.Threading; namespace Volo.Abp.DynamicProxy { @@ -19,12 +18,14 @@ namespace Volo.Abp.DynamicProxy public static void Add() { - IgnoredTypes.Locking(() => IgnoredTypes.AddIfNotContains(typeof(T))); + IgnoredTypes.AddIfNotContains(typeof(T)); } public static bool Contains(Type type, bool includeDerivedTypes = true) { - return includeDerivedTypes ? IgnoredTypes.Any(t => t.IsAssignableFrom(type)) : IgnoredTypes.Contains(type); + return includeDerivedTypes + ? IgnoredTypes.Any(t => t.IsAssignableFrom(type)) + : IgnoredTypes.Contains(type); } } } \ No newline at end of file From 6cf553c1b1eef1f9f6ebb1e994d35b8f9c5a482f Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sat, 11 Apr 2020 21:13:34 +0800 Subject: [PATCH 44/62] Add unit test --- .../Volo/Docs/ProjectAdminAppService_Tests.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs index 33d887de9b..3e4b62f876 100644 --- a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs +++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs @@ -45,7 +45,7 @@ namespace Volo.Docs var createProjectDto = new CreateProjectDto { Name = "ABP vNext", - ShortName = "ABP", + ShortName = "ABPvNext", Format = "md", DefaultDocumentName = "index", NavigationDocumentName = "docs-nav.json", @@ -120,5 +120,32 @@ namespace Volo.Docs (await _projectRepository.GetListAsync()).ShouldBeEmpty(); } + + [Fact] + public async Task Should_Throw_ProjectShortNameAlready_Exception_For_Duplicate_ShortName() + { + var createProjectDto = new CreateProjectDto + { + Name = "ABP vNext", + ShortName = "ABP", + Format = "md", + DefaultDocumentName = "index", + NavigationDocumentName = "docs-nav.json", + ParametersDocumentName = "docs-params.json", + MinimumVersion = "1", + MainWebsiteUrl = "abp.io", + LatestVersionBranchName = "", + DocumentStoreType = "GitHub", + ExtraProperties = new Dictionary() + }; + createProjectDto.ExtraProperties.Add("GitHubRootUrl", + "https://github.com/abpframework/abp/tree/{version}/docs/en/"); + createProjectDto.ExtraProperties.Add("GitHubAccessToken", "123456"); + createProjectDto.ExtraProperties.Add("GitHubUserAgent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + + //Act + await Assert.ThrowsAsync(() => + _projectAdminAppService.CreateAsync(createProjectDto)); + } } } From 2b211c1073e8ab55caa08384c5fd642af7f14f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 11 Apr 2020 16:17:21 +0300 Subject: [PATCH 45/62] Add validation for extra properties. --- .../Volo/Abp/Domain/Entities/AggregateRoot.cs | 18 +++ .../Abp/Data/HasExtraPropertiesExtensions.cs | 13 ++- .../Volo/Abp/Data/IHasExtraProperties.cs | 2 + .../Abp/ObjectExtending/ExtensibleObject.cs | 11 +- .../ExtensibleObjectValidator.cs | 75 ++++++++++++ .../ObjectExtensionPropertyInfo.cs | 5 + ...tensionPropertyInfoValidationExtensions.cs | 68 ----------- ...tExtensionPropertyInfoValidationOptions.cs | 25 ---- .../ExtensibleObjectValidator_Tests.cs | 109 ++++++++++++++++++ .../HasExtraPropertiesExtensions_Tests.cs | 4 + 10 files changed, 231 insertions(+), 99 deletions(-) create mode 100644 framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs delete mode 100644 framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationExtensions.cs delete mode 100644 framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationOptions.cs create mode 100644 framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs index 5d2ad56642..be02f3d398 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel.DataAnnotations; using Volo.Abp.Auditing; using Volo.Abp.Data; +using Volo.Abp.ObjectExtending; namespace Volo.Abp.Domain.Entities { @@ -56,6 +58,14 @@ namespace Volo.Abp.Domain.Entities { _distributedEvents.Clear(); } + + public virtual IEnumerable Validate(ValidationContext validationContext) + { + return ExtensibleObjectValidator.GetValidationErrors( + this, + validationContext + ); + } } [Serializable] @@ -115,5 +125,13 @@ namespace Volo.Abp.Domain.Entities { _distributedEvents.Clear(); } + + public virtual IEnumerable Validate(ValidationContext validationContext) + { + return ExtensibleObjectValidator.GetValidationErrors( + this, + validationContext + ); + } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs index 15915cc94b..2b75e5fc8f 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/HasExtraPropertiesExtensions.cs @@ -5,6 +5,8 @@ using Volo.Abp.Reflection; namespace Volo.Abp.Data { + //TODO: Move to Volo.Abp.Data.ObjectExtending namespace at v3.0 + public static class HasExtraPropertiesExtensions { public static bool HasProperty(this IHasExtraProperties source, string name) @@ -12,17 +14,18 @@ namespace Volo.Abp.Data return source.ExtraProperties.ContainsKey(name); } - public static object GetProperty(this IHasExtraProperties source, string name) + public static object GetProperty(this IHasExtraProperties source, string name, object defaultValue = null) { - return source.ExtraProperties?.GetOrDefault(name); + return source.ExtraProperties?.GetOrDefault(name) + ?? defaultValue; } - public static TProperty GetProperty(this IHasExtraProperties source, string name) + public static TProperty GetProperty(this IHasExtraProperties source, string name, TProperty defaultValue = default) { var value = source.GetProperty(name); - if (value == default) + if (value == null) { - return default; + return defaultValue; } if (TypeHelper.IsPrimitiveExtended(typeof(TProperty), includeEnums: true)) diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/IHasExtraProperties.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/IHasExtraProperties.cs index ccdb0b4137..1ba519680c 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/IHasExtraProperties.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/Data/IHasExtraProperties.cs @@ -2,6 +2,8 @@ namespace Volo.Abp.Data { + //TODO: Move to Volo.Abp.Data.ObjectExtending namespace at v3.0 + public interface IHasExtraProperties { Dictionary ExtraProperties { get; } diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs index 07243c9dca..c9c601fce9 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObject.cs @@ -1,11 +1,12 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using Volo.Abp.Data; namespace Volo.Abp.ObjectExtending { [Serializable] - public class ExtensibleObject : IHasExtraProperties + public class ExtensibleObject : IHasExtraProperties, IValidatableObject { public Dictionary ExtraProperties { get; protected set; } @@ -13,5 +14,13 @@ namespace Volo.Abp.ObjectExtending { ExtraProperties = new Dictionary(); } + + public virtual IEnumerable Validate(ValidationContext validationContext) + { + return ExtensibleObjectValidator.GetValidationErrors( + this, + validationContext + ); + } } } diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs new file mode 100644 index 0000000000..9b48798972 --- /dev/null +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using Volo.Abp.Data; +using Volo.Abp.DynamicProxy; + +namespace Volo.Abp.ObjectExtending +{ + public static class ExtensibleObjectValidator + { + [NotNull] + public static List GetValidationErrors( + [NotNull] IHasExtraProperties extensibleObject, + [CanBeNull] IServiceProvider serviceProvider = null) + { + var validationErrors = new List(); + + AddValidationErrors( + extensibleObject, + new ValidationContext( + extensibleObject, + serviceProvider, + new Dictionary() + ), + validationErrors + ); + + return validationErrors; + } + + public static void AddValidationErrors( + [NotNull] IHasExtraProperties extensibleObject, + [NotNull] ValidationContext validationContext, + [NotNull] List validationErrors) + { + Check.NotNull(extensibleObject, nameof(extensibleObject)); + Check.NotNull(validationContext, nameof(validationContext)); + Check.NotNull(validationErrors, nameof(validationErrors)); + + var objectType = ProxyHelper.UnProxy(extensibleObject).GetType(); + + var objectExtensionInfo = ObjectExtensionManager.Instance + .GetOrNull(objectType); + + if (objectExtensionInfo == null) + { + return; + } + + foreach (var propertyInfo in objectExtensionInfo.GetProperties()) + { + if (propertyInfo.ValidationAttributes.IsNullOrEmpty()) + { + continue; + } + + var validationContext2 = new ValidationContext(extensibleObject, validationContext, null) + { + DisplayName = propertyInfo.Name, + MemberName = propertyInfo.Name + }; + + foreach (var attribute in propertyInfo.ValidationAttributes) + { + var result = attribute.GetValidationResult(extensibleObject.GetProperty(propertyInfo.Name), validationContext2); + if (result != null) + { + validationErrors.Add(result); + } + } + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs index 2edae9a3f8..ce490b6264 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; namespace Volo.Abp.ObjectExtending @@ -15,6 +16,9 @@ namespace Volo.Abp.ObjectExtending [NotNull] public Type Type { get; } + [NotNull] + public List ValidationAttributes { get; } + /// /// Indicates whether to check the other side of the object mapping /// if it explicitly defines the property. This property is used in; @@ -42,6 +46,7 @@ namespace Volo.Abp.ObjectExtending Name = Check.NotNull(name, nameof(name)); Configuration = new Dictionary(); + ValidationAttributes = new List(); } } } diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationExtensions.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationExtensions.cs deleted file mode 100644 index 926e086d21..0000000000 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationExtensions.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; - -namespace Volo.Abp.ObjectExtending -{ - public static class ObjectExtensionPropertyInfoValidationExtensions - { - public const string ValidationConfigurationName = "Validation"; - - [NotNull] - public static ObjectExtensionPropertyInfo ConfigureValidation( - [NotNull] this ObjectExtensionPropertyInfo propertyExtension, - [NotNull] Action configureAction) - { - Check.NotNull(propertyExtension, nameof(propertyExtension)); - Check.NotNull(configureAction, nameof(configureAction)); - - configureAction(propertyExtension.GetOrAddValidationConfiguration()); - - return propertyExtension; - } - - [NotNull] - public static ObjectExtensionPropertyInfo AddValidationAttributes( - [NotNull] this ObjectExtensionPropertyInfo propertyExtension, - [NotNull] params ValidationAttribute[] validationAttributes) - { - Check.NotNull(propertyExtension, nameof(propertyExtension)); - Check.NotNullOrEmpty(validationAttributes, nameof(validationAttributes)); - - propertyExtension - .GetOrAddValidationConfiguration() - .ValidationAttributes - .AddRange(validationAttributes); - - return propertyExtension; - } - - [CanBeNull] - public static ObjectExtensionPropertyInfoValidationOptions GetValidationConfigurationOrNull( - [NotNull] this ObjectExtensionPropertyInfo propertyExtension) - { - Check.NotNull(propertyExtension, nameof(propertyExtension)); - - return propertyExtension.Configuration.GetOrDefault(ValidationConfigurationName) - as ObjectExtensionPropertyInfoValidationOptions; - } - - [NotNull] - public static ObjectExtensionPropertyInfoValidationOptions GetOrAddValidationConfiguration( - [NotNull] this ObjectExtensionPropertyInfo propertyExtension) - { - Check.NotNull(propertyExtension, nameof(propertyExtension)); - - var validationConfiguration = propertyExtension.GetValidationConfigurationOrNull(); - - if (validationConfiguration == null) - { - validationConfiguration = new ObjectExtensionPropertyInfoValidationOptions(propertyExtension); - propertyExtension.Configuration[ValidationConfigurationName] = validationConfiguration; - } - - return validationConfiguration; - } - } -} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationOptions.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationOptions.cs deleted file mode 100644 index e28efc14df..0000000000 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfoValidationOptions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using JetBrains.Annotations; - -namespace Volo.Abp.ObjectExtending -{ - public class ObjectExtensionPropertyInfoValidationOptions - { - [NotNull] - public ObjectExtensionPropertyInfo ExtensionProperty { get; } - - [NotNull] - public ObjectExtensionInfo ObjectExtension => ExtensionProperty.ObjectExtension; - - [NotNull] - public List ValidationAttributes { get; } - - public ObjectExtensionPropertyInfoValidationOptions( - [NotNull] ObjectExtensionPropertyInfo extensionProperty) - { - ExtensionProperty = Check.NotNull(extensionProperty, nameof(extensionProperty)); - ValidationAttributes = new List(); - } - } -} \ No newline at end of file diff --git a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs new file mode 100644 index 0000000000..35c419e7d8 --- /dev/null +++ b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs @@ -0,0 +1,109 @@ +using System.ComponentModel.DataAnnotations; +using Shouldly; +using Volo.Abp.Data; +using Volo.Abp.Threading; +using Xunit; + +namespace Volo.Abp.ObjectExtending +{ + public class ExtensibleObjectValidator_Tests + { + private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner(); + + static ExtensibleObjectValidator_Tests() + { + OneTimeRunner.Run(() => + { + ObjectExtensionManager.Instance + .AddOrUpdate(options => + { + options.AddOrUpdateProperty("Name", propertyInfo => + { + propertyInfo.ValidationAttributes.Add(new RequiredAttribute()); + propertyInfo.ValidationAttributes.Add(new StringLengthAttribute(64) { MinimumLength = 2 }); + }); + + options.AddOrUpdateProperty("Address", propertyInfo => + { + propertyInfo.ValidationAttributes.Add(new StringLengthAttribute(255)); + }); + + options.AddOrUpdateProperty("Age", propertyInfo => + { + propertyInfo.ValidationAttributes.Add(new RequiredAttribute()); + propertyInfo.ValidationAttributes.Add(new RangeAttribute(18, 99)); + }); + + options.AddOrUpdateProperty("IsMarried", propertyInfo => + { + + }); + }); + }); + } + + [Fact] + public void Should_Validate_If_The_Properties_Are_Valid() + { + ExtensibleObjectValidator + .GetValidationErrors( + new ExtensiblePersonObject + { + ExtraProperties = + { + {"Name", "John"}, + {"Age", "42"}, + } + } + ).Count.ShouldBe(0); //All Valid + } + + [Fact] + public void Should_Not_Validate_If_The_Properties_Are_Not_Valid() + { + ExtensibleObjectValidator + .GetValidationErrors( + new ExtensiblePersonObject() + ).Count.ShouldBe(2); //Name & Age + + ExtensibleObjectValidator + .GetValidationErrors( + new ExtensiblePersonObject + { + ExtraProperties = + { + {"Address", new string('x', 256) } + } + } + ).Count.ShouldBe(3); //Name, Age & Address + + ExtensibleObjectValidator + .GetValidationErrors( + new ExtensiblePersonObject + { + ExtraProperties = + { + {"Age", "42" } + } + } + ).Count.ShouldBe(1); //Name + + ExtensibleObjectValidator + .GetValidationErrors( + new ExtensiblePersonObject + { + ExtraProperties = + { + {"Address", new string('x', 256) }, + {"Age", "100" } + } + } + ).Count.ShouldBe(3); //Name, Age & Address + } + + private class ExtensiblePersonObject : ExtensibleObject + { + + } + } +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs index cc80c24bc0..29006c151e 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs @@ -16,18 +16,22 @@ namespace Volo.Abp.TestApp.Testing city.HasProperty("UnknownProperty").ShouldBeFalse(); city.GetProperty("UnknownProperty").ShouldBeNull(); city.GetProperty("UnknownProperty").ShouldBe(0); + city.GetProperty("UnknownProperty", 42).ShouldBe(42); city.SetProperty("IsHot", true); city.HasProperty("IsHot").ShouldBeTrue(); city.GetProperty("IsHot").ShouldBeTrue(); + city.GetProperty("IsHot").ShouldBeTrue(); city.SetProperty("IsHot", false); city.HasProperty("IsHot").ShouldBeTrue(); city.GetProperty("IsHot").ShouldBeFalse(); + city.GetProperty("IsHot", true).ShouldBeFalse(); city.RemoveProperty("IsHot"); city.HasProperty("IsHot").ShouldBeFalse(); city.GetProperty("IsHot").ShouldBeFalse(); + city.GetProperty("IsHot", true).ShouldBeFalse(); } } } \ No newline at end of file From 2d35a8be962cff5b0dfbd8dbaae7fb50557fa749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 11 Apr 2020 16:24:21 +0300 Subject: [PATCH 46/62] Refactor ExtensibleObjectValidator --- .../ExtensibleObjectValidator.cs | 57 ++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs index 9b48798972..f29f334a8d 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs @@ -1,6 +1,6 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Linq; using JetBrains.Annotations; using Volo.Abp.Data; using Volo.Abp.DynamicProxy; @@ -12,18 +12,14 @@ namespace Volo.Abp.ObjectExtending [NotNull] public static List GetValidationErrors( [NotNull] IHasExtraProperties extensibleObject, - [CanBeNull] IServiceProvider serviceProvider = null) + [CanBeNull] ValidationContext objectValidationContext = null) { var validationErrors = new List(); AddValidationErrors( extensibleObject, - new ValidationContext( - extensibleObject, - serviceProvider, - new Dictionary() - ), - validationErrors + validationErrors, + objectValidationContext ); return validationErrors; @@ -31,13 +27,21 @@ namespace Volo.Abp.ObjectExtending public static void AddValidationErrors( [NotNull] IHasExtraProperties extensibleObject, - [NotNull] ValidationContext validationContext, - [NotNull] List validationErrors) + [NotNull] List validationErrors, + [CanBeNull] ValidationContext objectValidationContext = null) { Check.NotNull(extensibleObject, nameof(extensibleObject)); - Check.NotNull(validationContext, nameof(validationContext)); Check.NotNull(validationErrors, nameof(validationErrors)); + if (objectValidationContext == null) + { + objectValidationContext = new ValidationContext( + extensibleObject, + null, + new Dictionary() + ); + } + var objectType = ProxyHelper.UnProxy(extensibleObject).GetType(); var objectExtensionInfo = ObjectExtensionManager.Instance @@ -50,23 +54,24 @@ namespace Volo.Abp.ObjectExtending foreach (var propertyInfo in objectExtensionInfo.GetProperties()) { - if (propertyInfo.ValidationAttributes.IsNullOrEmpty()) - { - continue; - } - - var validationContext2 = new ValidationContext(extensibleObject, validationContext, null) + if (propertyInfo.ValidationAttributes.Any()) { - DisplayName = propertyInfo.Name, - MemberName = propertyInfo.Name - }; + var propertyValidationContext = new ValidationContext(extensibleObject, objectValidationContext, null) + { + DisplayName = propertyInfo.Name, + MemberName = propertyInfo.Name + }; - foreach (var attribute in propertyInfo.ValidationAttributes) - { - var result = attribute.GetValidationResult(extensibleObject.GetProperty(propertyInfo.Name), validationContext2); - if (result != null) + foreach (var attribute in propertyInfo.ValidationAttributes) { - validationErrors.Add(result); + var result = attribute.GetValidationResult( + extensibleObject.GetProperty(propertyInfo.Name), + propertyValidationContext + ); + if (result != null) + { + validationErrors.Add(result); + } } } } From c69e2f12edf4eb8dccefb657d58cb4e996333455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 11 Apr 2020 17:01:04 +0300 Subject: [PATCH 47/62] Allow to write custom code to validate an extensible object. --- .../ExtensibleObjectValidator.cs | 79 +++++++++++++++++-- .../ObjectExtending/ObjectExtensionInfo.cs | 4 + .../ObjectExtensionPropertyInfo.cs | 4 + ...bjectExtensionPropertyValidationContext.cs | 61 ++++++++++++++ .../ObjectExtensionValidationContext.cs | 53 +++++++++++++ .../ExtensibleObjectValidator_Tests.cs | 73 +++++++++++++++-- 6 files changed, 263 insertions(+), 11 deletions(-) create mode 100644 framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyValidationContext.cs create mode 100644 framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionValidationContext.cs diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs index f29f334a8d..0c60f59699 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs @@ -52,20 +52,47 @@ namespace Volo.Abp.ObjectExtending return; } - foreach (var propertyInfo in objectExtensionInfo.GetProperties()) + AddPropertyValidationErrors( + extensibleObject, + validationErrors, + objectValidationContext, + objectExtensionInfo + ); + + ExecuteCustomObjectValidationActions( + extensibleObject, + validationErrors, + objectValidationContext, + objectExtensionInfo + ); + } + + private static void AddPropertyValidationErrors( + IHasExtraProperties extensibleObject, + List validationErrors, + ValidationContext objectValidationContext, + ObjectExtensionInfo objectExtensionInfo) + { + var properties = objectExtensionInfo.GetProperties(); + if (!properties.Any()) + { + return; + } + + foreach (var property in properties) { - if (propertyInfo.ValidationAttributes.Any()) + if (property.ValidationAttributes.Any()) { var propertyValidationContext = new ValidationContext(extensibleObject, objectValidationContext, null) { - DisplayName = propertyInfo.Name, - MemberName = propertyInfo.Name + DisplayName = property.Name, + MemberName = property.Name }; - foreach (var attribute in propertyInfo.ValidationAttributes) + foreach (var attribute in property.ValidationAttributes) { var result = attribute.GetValidationResult( - extensibleObject.GetProperty(propertyInfo.Name), + extensibleObject.GetProperty(property.Name), propertyValidationContext ); if (result != null) @@ -74,6 +101,46 @@ namespace Volo.Abp.ObjectExtending } } } + + if (property.Validators.Any()) + { + var context = new ObjectExtensionPropertyValidationContext( + property, + extensibleObject, + validationErrors, + objectValidationContext, + extensibleObject.GetProperty(property.Name) + ); + + foreach (var validator in property.Validators) + { + validator(context); + } + } + } + } + + private static void ExecuteCustomObjectValidationActions( + IHasExtraProperties extensibleObject, + List validationErrors, + ValidationContext objectValidationContext, + ObjectExtensionInfo objectExtensionInfo) + { + if (!objectExtensionInfo.Validators.Any()) + { + return; + } + + var context = new ObjectExtensionValidationContext( + objectExtensionInfo, + extensibleObject, + validationErrors, + objectValidationContext + ); + + foreach (var validator in objectExtensionInfo.Validators) + { + validator(context); } } } diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs index 89ecbab574..2159e06d5e 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionInfo.cs @@ -17,11 +17,15 @@ namespace Volo.Abp.ObjectExtending [NotNull] public Dictionary Configuration { get; } + [NotNull] + public List> Validators { get; } + public ObjectExtensionInfo([NotNull] Type type) { Type = Check.AssignableTo(type, nameof(type)); Properties = new Dictionary(); Configuration = new Dictionary(); + Validators = new List>(); } public virtual bool HasProperty(string propertyName) diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs index ce490b6264..e6dbbfd6ce 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyInfo.cs @@ -19,6 +19,9 @@ namespace Volo.Abp.ObjectExtending [NotNull] public List ValidationAttributes { get; } + [NotNull] + public List> Validators { get; } + /// /// Indicates whether to check the other side of the object mapping /// if it explicitly defines the property. This property is used in; @@ -47,6 +50,7 @@ namespace Volo.Abp.ObjectExtending Configuration = new Dictionary(); ValidationAttributes = new List(); + Validators = new List>(); } } } diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyValidationContext.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyValidationContext.cs new file mode 100644 index 0000000000..ab2a7ccb38 --- /dev/null +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionPropertyValidationContext.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using Volo.Abp.Data; + +namespace Volo.Abp.ObjectExtending +{ + public class ObjectExtensionPropertyValidationContext + { + /// + /// Related property extension information. + /// + [NotNull] + public ObjectExtensionPropertyInfo ExtensionPropertyInfo { get; } + + /// + /// Reference to the validating object. + /// + [NotNull] + public IHasExtraProperties ValidatingObject { get; } + + /// + /// Add validation errors to this list. + /// + [NotNull] + public List ValidationErrors { get; } + + /// + /// Validation context comes from the method. + /// + [NotNull] + public ValidationContext ValidationContext { get; } + + /// + /// The value of the validating property of the . + /// + [CanBeNull] + public object Value { get; } + + /// + /// Can be used to resolve services from the dependency injection container. + /// + [CanBeNull] + public IServiceProvider ServiceProvider => ValidationContext; + + public ObjectExtensionPropertyValidationContext( + [NotNull] ObjectExtensionPropertyInfo objectExtensionPropertyInfo, + [NotNull] IHasExtraProperties validatingObject, + [NotNull] List validationErrors, + [NotNull] ValidationContext validationContext, + [CanBeNull] object value) + { + ExtensionPropertyInfo = Check.NotNull(objectExtensionPropertyInfo, nameof(objectExtensionPropertyInfo)); + ValidatingObject = Check.NotNull(validatingObject, nameof(validatingObject)); + ValidationErrors = Check.NotNull(validationErrors, nameof(validationErrors)); + ValidationContext = Check.NotNull(validationContext, nameof(validationContext)); + Value = value; + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionValidationContext.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionValidationContext.cs new file mode 100644 index 0000000000..ce10f3fe3c --- /dev/null +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ObjectExtensionValidationContext.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; +using Volo.Abp.Data; + +namespace Volo.Abp.ObjectExtending +{ + public class ObjectExtensionValidationContext + { + /// + /// Related object extension information. + /// + [NotNull] + public ObjectExtensionInfo ObjectExtensionInfo { get; } + + /// + /// Reference to the validating object. + /// + [NotNull] + public IHasExtraProperties ValidatingObject { get; } + + /// + /// Add validation errors to this list. + /// + [NotNull] + public List ValidationErrors { get; } + + /// + /// Validation context comes from the method. + /// + [NotNull] + public ValidationContext ValidationContext { get; } + + /// + /// Can be used to resolve services from the dependency injection container. + /// + [CanBeNull] + public IServiceProvider ServiceProvider => ValidationContext; + + public ObjectExtensionValidationContext( + [NotNull] ObjectExtensionInfo objectExtensionInfo, + [NotNull] IHasExtraProperties validatingObject, + [NotNull] List validationErrors, + [NotNull] ValidationContext validationContext) + { + ObjectExtensionInfo = Check.NotNull(objectExtensionInfo, nameof(objectExtensionInfo)); + ValidatingObject = Check.NotNull(validatingObject, nameof(validatingObject)); + ValidationErrors = Check.NotNull(validationErrors, nameof(validationErrors)); + ValidationContext = Check.NotNull(validationContext, nameof(validationContext)); + } + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs index 35c419e7d8..8c27844e22 100644 --- a/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs +++ b/framework/test/Volo.Abp.ObjectExtending.Tests/Volo/Abp/ObjectExtending/ExtensibleObjectValidator_Tests.cs @@ -38,6 +38,43 @@ namespace Volo.Abp.ObjectExtending { }); + + options.AddOrUpdateProperty("Password", propertyInfo => + { + }); + + options.AddOrUpdateProperty("PasswordRepeat", propertyInfo => + { + propertyInfo.Validators.Add(context => + { + if (context.ValidatingObject.HasProperty("Password")) + { + if (context.ValidatingObject.GetProperty("Password") != + context.Value as string) + { + context.ValidationErrors.Add( + new ValidationResult( + "If you specify a password, then please correctly repeat it!", + new[] {"Password", "PasswordRepeat"} + ) + ); + } + } + }); + }); + + options.Validators.Add(context => + { + if (context.ValidatingObject.GetProperty("Name") == "BadValue") + { + context.ValidationErrors.Add( + new ValidationResult( + "Name can not be 'BadValue', sorry :(", + new[] { "Name" } + ) + ); + } + }); }); }); } @@ -64,7 +101,7 @@ namespace Volo.Abp.ObjectExtending ExtensibleObjectValidator .GetValidationErrors( new ExtensiblePersonObject() - ).Count.ShouldBe(2); //Name & Age + ).Count.ShouldBe(2); // Name & Age ExtensibleObjectValidator .GetValidationErrors( @@ -75,7 +112,7 @@ namespace Volo.Abp.ObjectExtending {"Address", new string('x', 256) } } } - ).Count.ShouldBe(3); //Name, Age & Address + ).Count.ShouldBe(3); // Name, Age & Address ExtensibleObjectValidator .GetValidationErrors( @@ -86,7 +123,7 @@ namespace Volo.Abp.ObjectExtending {"Age", "42" } } } - ).Count.ShouldBe(1); //Name + ).Count.ShouldBe(1); // Name ExtensibleObjectValidator .GetValidationErrors( @@ -97,8 +134,34 @@ namespace Volo.Abp.ObjectExtending {"Address", new string('x', 256) }, {"Age", "100" } } - } - ).Count.ShouldBe(3); //Name, Age & Address + } + ).Count.ShouldBe(3); // Name, Age & Address + + ExtensibleObjectValidator + .GetValidationErrors( + new ExtensiblePersonObject + { + ExtraProperties = + { + {"Name", "John"}, + {"Age", "42"}, + {"Password", "123"}, + {"PasswordRepeat", "1256"} + } + } + ).Count.ShouldBe(1); // PasswordRepeat != Password + + ExtensibleObjectValidator + .GetValidationErrors( + new ExtensiblePersonObject + { + ExtraProperties = + { + {"Name", "BadValue"}, + {"Age", "42"}, + } + } + ).Count.ShouldBe(1); //Name is 'BadValue'! } private class ExtensiblePersonObject : ExtensibleObject From adfc60d8ca68ed7546b8b6267517e620f163db3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 11 Apr 2020 17:12:00 +0300 Subject: [PATCH 48/62] Add IsValid and refactor ExtensibleObjectValidator --- .../ExtensibleObjectValidator.cs | 119 +++++++++++++----- 1 file changed, 86 insertions(+), 33 deletions(-) diff --git a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs index 0c60f59699..07d6556d10 100644 --- a/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs +++ b/framework/src/Volo.Abp.ObjectExtending/Volo/Abp/ObjectExtending/ExtensibleObjectValidator.cs @@ -9,6 +9,17 @@ namespace Volo.Abp.ObjectExtending { public static class ExtensibleObjectValidator { + [NotNull] + public static bool IsValid( + [NotNull] IHasExtraProperties extensibleObject, + [CanBeNull] ValidationContext objectValidationContext = null) + { + return GetValidationErrors( + extensibleObject, + objectValidationContext + ).Any(); + } + [NotNull] public static List GetValidationErrors( [NotNull] IHasExtraProperties extensibleObject, @@ -81,45 +92,87 @@ namespace Volo.Abp.ObjectExtending foreach (var property in properties) { - if (property.ValidationAttributes.Any()) - { - var propertyValidationContext = new ValidationContext(extensibleObject, objectValidationContext, null) - { - DisplayName = property.Name, - MemberName = property.Name - }; - - foreach (var attribute in property.ValidationAttributes) - { - var result = attribute.GetValidationResult( - extensibleObject.GetProperty(property.Name), - propertyValidationContext - ); - if (result != null) - { - validationErrors.Add(result); - } - } - } + AddPropertyValidationErrors(extensibleObject, validationErrors, objectValidationContext, property); + } + } - if (property.Validators.Any()) + private static void AddPropertyValidationErrors( + IHasExtraProperties extensibleObject, + List validationErrors, + ValidationContext objectValidationContext, + ObjectExtensionPropertyInfo property) + { + AddPropertyValidationAttributeErrors( + extensibleObject, + validationErrors, + objectValidationContext, + property + ); + + ExecuteCustomPropertyValidationActions( + extensibleObject, + validationErrors, + objectValidationContext, + property + ); + } + + private static void AddPropertyValidationAttributeErrors( + IHasExtraProperties extensibleObject, + List validationErrors, + ValidationContext objectValidationContext, + ObjectExtensionPropertyInfo property) + { + if (!property.ValidationAttributes.Any()) + { + return; + } + + var propertyValidationContext = new ValidationContext(extensibleObject, objectValidationContext, null) + { + DisplayName = property.Name, + MemberName = property.Name + }; + + foreach (var attribute in property.ValidationAttributes) + { + var result = attribute.GetValidationResult( + extensibleObject.GetProperty(property.Name), + propertyValidationContext + ); + + if (result != null) { - var context = new ObjectExtensionPropertyValidationContext( - property, - extensibleObject, - validationErrors, - objectValidationContext, - extensibleObject.GetProperty(property.Name) - ); - - foreach (var validator in property.Validators) - { - validator(context); - } + validationErrors.Add(result); } } } + private static void ExecuteCustomPropertyValidationActions( + IHasExtraProperties extensibleObject, + List validationErrors, + ValidationContext objectValidationContext, + ObjectExtensionPropertyInfo property) + { + if (!property.Validators.Any()) + { + return; + } + + var context = new ObjectExtensionPropertyValidationContext( + property, + extensibleObject, + validationErrors, + objectValidationContext, + extensibleObject.GetProperty(property.Name) + ); + + foreach (var validator in property.Validators) + { + validator(context); + } + } + private static void ExecuteCustomObjectValidationActions( IHasExtraProperties extensibleObject, List validationErrors, From ec1b031a72c1cedeef5288494cd6b33775fa1a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 11 Apr 2020 17:17:19 +0300 Subject: [PATCH 49/62] Fix HasExtraPropertiesExtensions_Tests --- .../Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs index 29006c151e..918e96581b 100644 --- a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/HasExtraPropertiesExtensions_Tests.cs @@ -31,7 +31,7 @@ namespace Volo.Abp.TestApp.Testing city.RemoveProperty("IsHot"); city.HasProperty("IsHot").ShouldBeFalse(); city.GetProperty("IsHot").ShouldBeFalse(); - city.GetProperty("IsHot", true).ShouldBeFalse(); + city.GetProperty("IsHot", true).ShouldBeTrue(); } } } \ No newline at end of file From bc3676cfd45d43425af67f3a89ff97a686ce4b0f Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sat, 11 Apr 2020 22:39:25 +0800 Subject: [PATCH 50/62] Translate link-list document --- docs/en/UI/Common/Utils/Linked-List.md | 17 +- .../UI/AspNetCore/Libraries/DatatablesNet.md | 3 + docs/zh-Hans/UI/Common/Utils/Linked-List.md | 1456 +++++++++++++++++ docs/zh-Hans/docs-nav.json | 9 + 4 files changed, 1474 insertions(+), 11 deletions(-) create mode 100644 docs/zh-Hans/UI/AspNetCore/Libraries/DatatablesNet.md create mode 100644 docs/zh-Hans/UI/Common/Utils/Linked-List.md diff --git a/docs/en/UI/Common/Utils/Linked-List.md b/docs/en/UI/Common/Utils/Linked-List.md index 5da9cc10e9..c53b3edd06 100644 --- a/docs/en/UI/Common/Utils/Linked-List.md +++ b/docs/en/UI/Common/Utils/Linked-List.md @@ -1384,7 +1384,7 @@ list.forEach((node, index) => console.log(node.value + index)); #### \*\[Symbol.iterator\]\(\) -A linked list is iterable. In other words, you may use methods like `for...of` on it. +链表是可迭代的. 换句话说你可以使用诸如`for ... of`之类的方法. ```js list.addTailMany(['a', 'b', 'c']); @@ -1408,7 +1408,7 @@ for(const node of list) { toArray(): T\[\] ``` -Converts a linked list to an array of values: +转换链表值为数组: ```js list.addTailMany(['a', 'b', 'c']); @@ -1430,7 +1430,7 @@ arr === ['a', 'b', 'c'] toNodeArray(): T\[\] ``` -Converts a linked list to an array of nodes: +转换链表节点为数组: ```js list.addTailMany(['a', 'b', 'c']); @@ -1454,7 +1454,7 @@ arr[2].value === 'a' toString(): string ``` -Converts a linked list to a string representation of nodes and their relations: +将链表转换为节点及其关系的字符串表示形式: ```js list.addTailMany(['a', 2, 'c', { k: 4, v: 'd' }]); @@ -1468,9 +1468,7 @@ str === '"a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}' */ ``` - - -You may pass a custom mapper function to map values before stringifying them: +你可以在对值进行字符串化之前通过自定义映射器函数来映射值: ```js list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }, { x: 5 }]).tail(); @@ -1482,7 +1480,4 @@ const str = list.toString(value => value.x); /* str === '1 <-> 2 <-> 3 <-> 4 <-> 5' */ -``` - - - +``` \ No newline at end of file diff --git a/docs/zh-Hans/UI/AspNetCore/Libraries/DatatablesNet.md b/docs/zh-Hans/UI/AspNetCore/Libraries/DatatablesNet.md new file mode 100644 index 0000000000..029691e720 --- /dev/null +++ b/docs/zh-Hans/UI/AspNetCore/Libraries/DatatablesNet.md @@ -0,0 +1,3 @@ +# ABP ASP.NET Core UI Datatables.Net 集成 + +TODO \ No newline at end of file diff --git a/docs/zh-Hans/UI/Common/Utils/Linked-List.md b/docs/zh-Hans/UI/Common/Utils/Linked-List.md new file mode 100644 index 0000000000..c468275bba --- /dev/null +++ b/docs/zh-Hans/UI/Common/Utils/Linked-List.md @@ -0,0 +1,1456 @@ +# 链表 (双向)) + +Core模块提供了称为[双链表](https://en.wikipedia.org/wiki/Doubly_linked_list)的实用数据结构. 简而言之双向链表是一系列记录(又称节点),这些记录具有上一个节点,下一个节点及其自身值(或数据)的信息. + +## 入门 + +要创建一个双向链表,你需要做的就是导入和创建它的一个新的实例: + +```js +import { LinkedList } from '@abp/ng.core'; + +const list = new LinkedList(); +``` + +构造函数没有任何参数. + +## 用法 + +### 如何添加新节点 + +有几种方法可以在链表中创建新节点,这些方法都可以单独使用,也可以通过 `add` 和 `addMany` 方法. + + +#### addHead(value) + +```js +addHead(value: T): ListNode\ +``` + +将给定值添加到链表的第一个节点: + +```js +list.addHead('a'); + +// "a" + +list.addHead('b'); + +// "b" <-> "a" + +list.addHead('c'); + +// "c" <-> "b" <-> "a" +``` + + + +#### addManyHead(values) + +```js +addManyHead(values: T\[\]): ListNode\\[\] +``` + +将给定的多个值添加到链表的第一个节点: + +```js +list.addManyHead(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +list.addManyHead(['x', 'y', 'z']); + +// "x" <-> "y" <-> "z" <-> "a" <-> "b" <-> "c" +``` + + + +#### addTail(value) + +```js +addTail(value: T): ListNode\ +``` + +将给定值添加到链表的最后一个节点: + +```js +list.addTail('a'); + +// "a" + +list.addTail('b'); + +// "a" <-> "b" + +list.addTail('c'); + +// "a" <-> "b" <-> "c" +``` + + + +#### addManyTail(values) + +```js +addManyTail(values: T\[\]): ListNode\\[\] +``` + +将给定多个值添加到链表的最后一个节点: + +```js +list.addManyTail(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +list.addManyTail(['x', 'y', 'z']); + +// "a" <-> "b" <-> "c" <-> "x" <-> "y" <-> "z" +``` + +#### addAfter(value, previousValue, compareFn) + +```js +addAfter(value: T, previousValue: T, compareFn = compare): ListNode\ +``` + +添加给定值到previousValue节点后: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addAfter('x', 'b'); + +// "a" <-> "b" <-> "x" <-> "b" <-> "c" +``` + + +你可以自定义比较器: + +```js +list.addTail({ x: 1 }); +list.addTail({ x: 2 }); +list.addTail({ x: 3 }); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.addAfter( + { x: 0 }, + 2, + (value, searchedValue) => value.x === searchedValue +); + +// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} +``` + + +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + +#### addManyAfter(values, previousValue, compareFn) + +```js +addManyAfter(values: T\[\], previousValue: T, compareFn = compare): ListNode\\[\] +``` + +添加给定的多个值到previousValue节点后: + +```js +list.addManyTail(['a', 'b', 'b', 'c']); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addManyAfter(['x', 'y'], 'b'); + +// "a" <-> "b" <-> "x" <-> "y" <-> "b" <-> "c" +``` + +你可以自定义比较器: + +```js +list.addManyTail([{ x: 1 },{ x: 2 },{ x: 3 }]); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.addManyAfter( + [{ x: 4 }, { x: 5 }], + 2, + (value, searchedValue) => value.x === searchedValue +); + +// {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3} +``` + +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + +#### addBefore(value, nextValue, compareFn) + +```js +addBefore(value: T, nextValue: T, compareFn = compare): ListNode\ +``` + +添加给值到previousValue节点前: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addBefore('x', 'b'); + +// "a" <-> "x" <-> "b" <-> "b" <-> "c" +``` + +你可以自定义比较器: + +```js +list.addTail({ x: 1 }); +list.addTail({ x: 2 }); +list.addTail({ x: 3 }); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.addBefore( + { x: 0 }, + 2, + (value, searchedValue) => value.x === searchedValue +); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} +``` + +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +#### addManyBefore(values, nextValue, compareFn) + +```js +addManyBefore(values: T\[\], nextValue: T, compareFn = compare): ListNode\\[\] +``` + + +添加给定的多个值到previousValue节点前: + +```js +list.addManyTail(['a', 'b', 'b', 'c']); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addManyBefore(['x', 'y'], 'b'); + +// "a" <-> "x" <-> "y" <-> "b" <-> "b" <-> "c" +``` + + + +你可以自定义比较器 + +```js +list.addManyTail([{ x: 1 },{ x: 2 },{ x: 3 }]); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list.addManyBefore( + [{ x: 4 }, { x: 5 }], + 2, + (value, searchedValue) => value.x === searchedValue +); + +// {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3} +``` + + + +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +#### addByIndex(value, position) + +```js +addByIndex(value: T, position: number): ListNode\ +``` + +在链表的指定位置添加节点: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +list.addByIndex('x', 2); + +// "a" <-> "b" <-> "x" <-> "c" +``` + +它也适用于负索引: + +```js +list.addTail('a'); +list.addTail('b'); +list.addTail('c'); + +// "a" <-> "b" <-> "c" + +list.addByIndex('x', -1); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + +#### addManyByIndex(values, position) + +```js +addManyByIndex(values: T\[\], position: number): ListNode\\[\] +``` + +添加多个节点到链表的指定位置: + +```js +list.addManyTail(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +list.addManyByIndex(['x', 'y'], 2); + +// "a" <-> "b" <-> "x" <-> "y" <-> "c" +``` + +它也适用于负索引: + +```js +list.addManyTail(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +list.addManyByIndex(['x', 'y'], -1); + +// "a" <-> "b" <-> "x" <-> "y" <-> "c" +``` + + + +#### add(value).head() + +```js +add(value: T).head(): ListNode\ +``` + +将添加的节点移动到链表头: + +```js +list.add('a').head(); + +// "a" + +list.add('b').head(); + +// "b" <-> "a" + +list.add('c').head(); + +// "c" <-> "b" <-> "a" +``` + + + +> 它是 `addHead` 的替代API. + + + +#### add(value).tail() + +```js +add(value: T).tail(): ListNode\ +``` + +将添加的节点移动到链表尾: + +```js +list.add('a').tail(); + +// "a" + +list.add('b').tail(); + +// "a" <-> "b" + +list.add('c').tail(); + +// "a" <-> "b" <-> "c" +``` + + + +> 它是 `addTail` 的替代API. + + + +#### add(value).after(previousValue, compareFn) + +```js +add(value: T).after(previousValue: T, compareFn = compare): ListNode\ +``` + +将添加的节点移动到指定节点后: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "b" <-> "c" + +list.add('x').after('b'); + +// "a" <-> "b" <-> "x" <-> "b" <-> "c" +``` + + + +你可以自定义比较器 + +```js +list.add({ x: 1 }).tail(); +list.add({ x: 2 }).tail(); +list.add({ x: 3 }).tail(); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list + .add({ x: 0 }) + .after(2, (value, searchedValue) => value.x === searchedValue); + +// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} +``` + + + +> 它是 `addAfter` 的替代API. +> +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +#### add(value).before(nextValue, compareFn) + +```js +add(value: T).before(nextValue: T, compareFn = compare): ListNode\ +``` + +将添加的节点移动到指定节点前: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "b" <-> "c" + +list.add('x').before('b'); + +// "a" <-> "x" <-> "b" <-> "b" <-> "c" +``` + + + +你可以自定义比较器 + +```js +list.add({ x: 1 }).tail(); +list.add({ x: 2 }).tail(); +list.add({ x: 3 }).tail(); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list + .add({ x: 0 }) + .before(2, (value, searchedValue) => value.x === searchedValue); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":3} +``` + + + +> 它是 `addBefore` 的替代API. +> +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +#### add(value).byIndex(position) + +```js +add(value: T).byIndex(position: number): ListNode\ +``` + +将添加的节点移动到链表指定位置: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "c" + +list.add('x').byIndex(2); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + +它也适用于负索引: + +```js +list.add('a').tail(); +list.add('b').tail(); +list.add('c').tail(); + +// "a" <-> "b" <-> "c" + +list.add('x').byIndex(-1); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + +> 它是 `addByIndex` 的替代API. + + + +#### addMany(values).head() + +```js +addMany(values: T\[\]).head(): ListNode\\[\] +``` + +将添加的多个节点移动到链表头: + +```js +list.addMany(['a', 'b', 'c']).head(); + +// "a" <-> "b" <-> "c" + +list.addMany(['x', 'y', 'z']).head(); + +// "x" <-> "y" <-> "z" <-> "a" <-> "b" <-> "c" +``` + + + +> 它是 `addManyHead` 的替代API. + + + +#### addMany(values).tail() + +```js +addMany(values: T\[\]).tail(): ListNode\\[\] +``` + +将添加的多个节点移动到链表尾: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.addMany(['x', 'y', 'z']).tail(); + +// "a" <-> "b" <-> "c" <-> "x" <-> "y" <-> "z" +``` + + + +> 它是 `addManyTail` 的替代API. + + + +#### addMany(values).after(previousValue, compareFn) + +```js +addMany(values: T\[\]).after(previousValue: T, compareFn = compare): ListNode\\[\] +``` + +将添加的多个节点移动到指定节点后: + +```js +list.addMany(['a', 'b', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addMany(['x', 'y']).after('b'); + +// "a" <-> "b" <-> "x" <-> "y" <-> "b" <-> "c" +``` + + + +你可以自定义比较器 + +```js +list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list + .addMany([{ x: 4 }, { x: 5 }]) + .after(2, (value, searchedValue) => value.x === searchedValue); + +// {"x":1} <-> {"x":2} <-> {"x":4} <-> {"x":5} <-> {"x":3} +``` + + + +> 它是 `addManyAfter` 的替代API. +> +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +#### addMany(values).before(nextValue, compareFn) + +```js +addMany(values: T\[\]).before(nextValue: T, compareFn = compare): ListNode\\[\] +``` + +将添加的多个节点移动到指定节点前: + +```js +list.addMany(['a', 'b', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "b" <-> "c" + +list.addMany(['x', 'y']).before('b'); + +// "a" <-> "x" <-> "y" <-> "b" <-> "b" <-> "c" +``` + + + +你可以自定义比较器 + +```js +list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }]).tail(); + +// {"x":1} <-> {"x":2} <-> {"x":3} + +list + .addMany([{ x: 4 }, { x: 5 }]) + .before(2, (value, searchedValue) => value.x === searchedValue); + +// {"x":1} <-> {"x":4} <-> {"x":5} <-> {"x":2} <-> {"x":3} +``` + + + +> 它是 `addManyBefore` 的替代API. +> +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +#### addMany(values).byIndex(position) + +```js +addMany(values: T\[\]).byIndex(position: number): ListNode\\[\] +``` + +将添加的多个节点移动到链表的指定位置: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.addMany(['x', 'y']).byIndex(2); + +// "a" <-> "b" <-> "x" <-> "y" <-> "c" +``` + + +它也适用于负索引: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.addMany(['x', 'y']).byIndex(-1); + +// "a" <-> "b" <-> "x" <-> "y" <-> "c" +``` + + + +> 它是 `addManyByIndex` 的替代API. + + + +### 如何删除节点 + +有几种方法可以在链表中删除节点,这些方法都可以单独使用,也可以通过 `drop` 方法. + + + +#### dropHead() + +```js +dropHead(): ListNode\ | undefined +``` + +删除链表的第一个节点: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropHead(); + +// "b" <-> "c" +``` + + + +#### dropManyHead(count) + +```js +dropManyHead(count: number): ListNode\\[\] +``` + +删除指定数量的链表的头节点: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropManyHead(2); + +// "c" +``` + + + +#### dropTail() + +```js +dropTail(): ListNode\ | undefined +``` + +删除链表的最后一个节点: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropTail(); + +// "a" <-> "b" +``` + + + +#### dropManyTail(count) + +```js +dropManyTail(count: number): ListNode\\[\] +``` + +删除指定数量的链表的尾节点: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropManyTail(2); + +// "a" +``` + + + +#### dropByIndex(position) + +```js +dropByIndex(position: number): ListNode\ | undefined +``` + +删除链表中给定位置的节点: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropByIndex(1); + +// "a" <-> "c" +``` + + +它也适用于负索引: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropByIndex(-2); + +// "a" <-> "c" +``` + + + +#### dropManyByIndex(count, position) + +```js +dropManyByIndex(count: number, position: number): ListNode\\[\] +``` + +删除链表中给定位置与数量的多个节点: + +```js +list.addMany(['a', 'b', 'c', 'd']).tail(); + +// "a" <-> "b" <-> "c" <-> "d + +list.dropManyByIndex(2, 1); + +// "a" <-> "d" +``` + + + +它也适用于负索引: + +```js +list.addMany(['a', 'b', 'c', 'd']).tail(); + +// "a" <-> "b" <-> "c" <-> "d + +list.dropManyByIndex(2, -2); + +// "a" <-> "d" +``` + + + +#### dropByValue(value, compareFn) + +```js +dropByValue(value: T, compareFn = compare): ListNode\ | undefined +``` + +删除链表中含有给定值的第一个节点: + +```js +list.addMany(['a', 'x', 'b', 'x', 'c']).tail(); + +// "a" <-> "x" <-> "b" <-> "x" <-> "c" + +list.dropByValue('x'); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + +你可以自定义比较器 + +```js +list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + +list.dropByValue(0, (value, searchedValue) => value.x === searchedValue); + +// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} +``` + + + +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +#### dropByValueAll(value, compareFn) + +```js +dropByValueAll(value: T, compareFn = compare): ListNode\\[\] +``` + +删除链表中含有给定值的所有节点: + +```js +list.addMany(['a', 'x', 'b', 'x', 'c']).tail(); + +// "a" <-> "x" <-> "b" <-> "x" <-> "c" + +list.dropByValueAll('x'); + +// "a" <-> "b" <-> "c" +``` + +你可以自定义比较器 + +```js +list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + +list.dropByValue(0, (value, searchedValue) => value.x === searchedValue); + +// {"x":1} <-> {"x":2} <-> {"x":3} +``` + + + +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +#### drop().head() + +```js +drop().head(): ListNode\ | undefined +``` + +删除链表的头节点: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.drop().head(); + +// "b" <-> "c" +``` + + + +> 它是 `dropHead` 的替代API. + + + +#### drop().tail() + +```js +drop().tail(): ListNode\ | undefined +``` + +删除链表的尾节点: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.drop().tail(); + +// "a" <-> "b" +``` + + + +> 它是 `dropTail` 的替代API. + + + +#### drop().byIndex(position) + +```js +drop().byIndex(position: number): ListNode\ | undefined +``` + +删除链表指定位置的节点: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.drop().byIndex(1); + +// "a" <-> "c" +``` + + + +它也适用于负索引: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.drop().byIndex(-2); + +// "a" <-> "c" +``` + + + +> 它是 `dropByIndex` 的替代API. + + + +#### drop().byValue(value, compareFn) + +```js +drop().byValue(value: T, compareFn = compare): ListNode\ | undefined +``` + +删除链表中含有给定值的第一个节点: + +```js +list.addMany(['a', 'x', 'b', 'x', 'c']).tail(); + +// "a" <-> "x" <-> "b" <-> "x" <-> "c" + +list.drop().byValue('x'); + +// "a" <-> "b" <-> "x" <-> "c" +``` + + + +你可以自定义比较器 + +```js +list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + +list + .drop() + .byValue(0, (value, searchedValue) => value.x === searchedValue); + +// {"x":1} <-> {"x":2} <-> {"x":0} <-> {"x":3} +``` + + + +> 它是 `dropByValue` 的替代API. +> +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +#### drop().byValueAll(value, compareFn) + +```js +drop().byValueAll(value: T, compareFn = compare): ListNode\\[\] +``` + +删除链表中含有给定值的所有节点: + +```js +list.addMany(['a', 'x', 'b', 'x', 'c']).tail(); + +// "a" <-> "x" <-> "b" <-> "x" <-> "c" + +list.drop().byValueAll('x'); + +// "a" <-> "b" <-> "c" +``` + + + +你可以自定义比较器 + +```js +list.addMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]).tail(); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + +list + .drop() + .byValueAll(0, (value, searchedValue) => value.x === searchedValue); + +// {"x":1} <-> {"x":2} <-> {"x":3} +``` + + + +> 它是 `dropByValueAll` 的替代API. +> +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +#### dropMany(count).head() + +```js +dropMany(count: number).head(): ListNode\\[\] +``` + +删除链表中指定数量的头节点: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropMany(2).head(); + +// "c" +``` + + + +> 它是 `dropManyHead` 的替代API. + + + +#### dropMany(count).tail() + +```js +dropMany(count: number).tail(): ListNode\\[\] +``` + +删除链表中指定数量的尾节点:: + +```js +list.addMany(['a', 'b', 'c']).tail(); + +// "a" <-> "b" <-> "c" + +list.dropMany(2).tail(); + +// "a" +``` + + + +> 它是 `dropManyTail` 的替代API. + + + +#### dropMany(count).byIndex(position) + +```js +dropMany(count: number).byIndex(position: number): ListNode\\[\] +``` + +删除链表中指定位置和数量的节点: + +```js +list.addMany(['a', 'b', 'c', 'd']).tail(); + +// "a" <-> "b" <-> "c" <-> "d + +list.dropMany(2).byIndex(1); + +// "a" <-> "d" +``` + + + +它也适用于负索引: + +```js +list.addMany(['a', 'b', 'c', 'd']).tail(); + +// "a" <-> "b" <-> "c" <-> "d + +list.dropMany(2).byIndex(-2); + +// "a" <-> "d" +``` + + + +> 它是 `dropManyByIndex` 的替代API. + + + +### 如何查找节点 + +有几个方法找到链表特定节点. + + + +#### find(predicate) + +```js +find(predicate: ListIteratorFunction\): ListNode\ | undefined +``` + +从链表中找到与给定谓词匹配的第一个节点: + +```js +list.addTailMany(['a', 'b', 'b', 'c']); + +// "a" <-> "b" <-> "b" <-> "c" + +const found = list.find(node => node.value === 'b'); + +/* +found.value === "b" +found.previous.value === "a" +found.next.value === "b" +*/ +``` + + + +#### findIndex(predicate) + +```js +findIndex(predicate: ListIteratorFunction\): number +``` + +从链表中找到与给定谓词匹配的第一个节点的位置: + +```js +list.addTailMany(['a', 'b', 'b', 'c']); + +// "a" <-> "b" <-> "b" <-> "c" + +const i0 = list.findIndex(node => node.next && node.next.value === 'b'); +const i1 = list.findIndex(node => node.value === 'b'); +const i2 = list.findIndex(node => node.previous && node.previous.value === 'b'); +const i3 = list.findIndex(node => node.value === 'x'); + +/* +i0 === 0 +i1 === 1 +i2 === 2 +i3 === -1 +*/ +``` + + + +#### get(position) + +```js +get(position: number): ListNode\ | undefined +``` + +查找并返回链表中特定位置的节点: + +```js +list.addTailMany(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +const found = list.get(1); + +/* +found.value === "b" +found.previous.value === "a" +found.next.value === "c" +*/ +``` + + + +#### indexOf(value, compareFn) + +```js +indexOf(value: T, compareFn = compare): number +``` + +在链表中找到匹配给定值的第一个节点位置: + +```js +list.addTailMany(['a', 'b', 'b', 'c']); + +// "a" <-> "b" <-> "b" <-> "c" + +const i0 = list.indexOf('a'); +const i1 = list.indexOf('b'); +const i2 = list.indexOf('c'); +const i3 = list.indexOf('x'); + +/* +i0 === 0 +i1 === 1 +i2 === 3 +i3 === -1 +*/ +``` + + + +你可以自定义比较器 + +```js +list.addTailMany([{ x: 1 }, { x: 0 }, { x: 2 }, { x: 0 }, { x: 3 }]); + +// {"x":1} <-> {"x":0} <-> {"x":2} <-> {"x":0} <-> {"x":3} + +const i0 = indexOf(1, (value, searchedValue) => value.x === searchedValue); +const i1 = indexOf(2, (value, searchedValue) => value.x === searchedValue); +const i2 = indexOf(3, (value, searchedValue) => value.x === searchedValue); +const i3 = indexOf(0, (value, searchedValue) => value.x === searchedValue); +const i4 = indexOf(4, (value, searchedValue) => value.x === searchedValue); + +/* +i0 === 0 +i1 === 2 +i2 === 4 +i3 === 1 +i4 === -1 +*/ +``` + + + +> 默认的比较函数检查深度相等性,因此你几乎不需要传递该参数. + + + +### 如何检查所有节点 + +有几种方法来遍历或显示一个链表. + + + +#### forEach(callback) + +```js +forEach(callback: ListIteratorFunction\): void +``` + +从头到尾在链表中的所有节点上运行回调函数: + +```js +list.addTailMany(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +list.forEach((node, index) => console.log(node.value + index)); + +// 'a0' +// 'b1' +// 'c2' +``` + +#### \*\[Symbol.iterator\]\(\) + +A linked list is iterable. In other words, you may use methods like `for...of` on it. + +```js +list.addTailMany(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +for(const node of list) { + console.log(node.value); +} + +// 'a' +// 'b' +// 'c' +``` + + + +#### toArray() + +```js +toArray(): T\[\] +``` + +Converts a linked list to an array of values: + +```js +list.addTailMany(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +const arr = list.toArray(); + +/* +arr === ['a', 'b', 'c'] +*/ +``` + + + +#### toNodeArray() + +```js +toNodeArray(): T\[\] +``` + +Converts a linked list to an array of nodes: + +```js +list.addTailMany(['a', 'b', 'c']); + +// "a" <-> "b" <-> "c" + +const arr = list.toNodeArray(); + +/* +arr[0].value === 'a' +arr[1].value === 'a' +arr[2].value === 'a' +*/ +``` + + + +#### toString() + +```js +toString(): string +``` + +Converts a linked list to a string representation of nodes and their relations: + +```js +list.addTailMany(['a', 2, 'c', { k: 4, v: 'd' }]); + +// "a" <-> 2 <-> "c" <-> {"k":4,"v":"d"} + +const str = list.toString(); + +/* +str === '"a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}' +*/ +``` + + + +You may pass a custom mapper function to map values before stringifying them: + +```js +list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }, { x: 5 }]).tail(); + +// {"x":1} <-> {"x":2} <-> {"x":3} <-> {"x":4} <-> {"x":5} + +const str = list.toString(value => value.x); + +/* +str === '1 <-> 2 <-> 3 <-> 4 <-> 5' +*/ +``` + + + diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index f01ce04bc9..47e0c1b05e 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -313,6 +313,15 @@ "path": "UI/Angular/Component-Replacement.md" } ] + }, + { + "text": "公共", + "items": [ + { + "text": "链表 (双向)", + "path": "UI/Common/Utils/Linked-List.md" + } + ] } ] }, From 61f32fa3aa8b577d34c7ab694ced6518ad6dcdc9 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sat, 11 Apr 2020 23:00:31 +0800 Subject: [PATCH 51/62] Normalize shortName --- .../Volo.Docs.Domain/Volo/Docs/Projects/Project.cs | 7 +++++++ .../Volo/Docs/Projects/EfCoreProjectRepository.cs | 13 +++++++++++-- .../Volo/Docs/Projects/MongoProjectRepository.cs | 13 +++++++++++-- .../Volo/Docs/ProjectAdminAppService_Tests.cs | 2 +- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs index f1cfd0d777..8bc36a2106 100644 --- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs +++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/Projects/Project.cs @@ -76,6 +76,8 @@ namespace Volo.Docs.Projects ParametersDocumentName = Check.NotNullOrWhiteSpace(parametersDocumentName, nameof(parametersDocumentName)); ExtraProperties = new Dictionary(); + + NormalizeShortName(); } public void SetName(string name) @@ -102,5 +104,10 @@ namespace Volo.Docs.Projects { DefaultDocumentName = Check.NotNullOrWhiteSpace(defaultDocumentName, nameof(defaultDocumentName)); } + + private void NormalizeShortName() + { + ShortName = ShortName.ToLower(); + } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs index 0611624029..835172e679 100644 --- a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs +++ b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs @@ -29,7 +29,9 @@ namespace Volo.Docs.Projects public async Task GetByShortNameAsync(string shortName) { - var project = await DbSet.FirstOrDefaultAsync(p => p.ShortName == shortName); + var normalizeShortName = NormalizeShortName(shortName); + + var project = await DbSet.FirstOrDefaultAsync(p => p.ShortName == normalizeShortName); if (project == null) { @@ -41,7 +43,14 @@ namespace Volo.Docs.Projects public async Task ShortNameExistsAsync(string shortName) { - return await DbSet.AnyAsync(x => x.ShortName == shortName); + var normalizeShortName = NormalizeShortName(shortName); + + return await DbSet.AnyAsync(x => x.ShortName == normalizeShortName); + } + + private string NormalizeShortName(string shortName) + { + return shortName.ToLower(); } } } \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs index 1dbb646405..f1312b9e5d 100644 --- a/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs +++ b/modules/docs/src/Volo.Docs.MongoDB/Volo/Docs/Projects/MongoProjectRepository.cs @@ -30,7 +30,9 @@ namespace Volo.Docs.Projects public async Task GetByShortNameAsync(string shortName) { - var project = await GetMongoQueryable().FirstOrDefaultAsync(p => p.ShortName == shortName); + var normalizeShortName = NormalizeShortName(shortName); + + var project = await GetMongoQueryable().FirstOrDefaultAsync(p => p.ShortName == normalizeShortName); if (project == null) { @@ -42,7 +44,14 @@ namespace Volo.Docs.Projects public async Task ShortNameExistsAsync(string shortName) { - return await GetMongoQueryable().AnyAsync(x => x.ShortName == shortName); + var normalizeShortName = NormalizeShortName(shortName); + + return await GetMongoQueryable().AnyAsync(x => x.ShortName == normalizeShortName); + } + + private string NormalizeShortName(string shortName) + { + return shortName.ToLower(); } } } \ No newline at end of file diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs index 3e4b62f876..442a72f18b 100644 --- a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs +++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs @@ -67,7 +67,7 @@ namespace Volo.Docs //Assert projectDto.ShouldNotBeNull(); projectDto.Name.ShouldBe(createProjectDto.Name); - projectDto.ShortName.ShouldBe(createProjectDto.ShortName); + projectDto.ShortName.ShouldBe(createProjectDto.ShortName.ToLower()); projectDto.Format.ShouldBe(createProjectDto.Format); projectDto.DefaultDocumentName.ShouldBe(createProjectDto.DefaultDocumentName); projectDto.NavigationDocumentName.ShouldBe(createProjectDto.NavigationDocumentName); From d255b7e9a4fc7b88f3c0c7ace5d03ce4dd555283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 11 Apr 2020 19:37:26 +0300 Subject: [PATCH 52/62] Add Validation document for the object extensions. --- docs/en/Object-Extensions.md | 112 ++++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 7 deletions(-) diff --git a/docs/en/Object-Extensions.md b/docs/en/Object-Extensions.md index bbcb96374c..6eff328c85 100644 --- a/docs/en/Object-Extensions.md +++ b/docs/en/Object-Extensions.md @@ -156,11 +156,9 @@ ObjectExtensionManager.Instance ); ```` -#### Property Configuration +### Property Configuration -`AddOrUpdateProperty` can also get an action that can perform additional configuration on the property definition. - -Example: +`AddOrUpdateProperty` can also get an action that can perform additional configuration on the property definition: ````csharp ObjectExtensionManager.Instance @@ -168,13 +166,113 @@ ObjectExtensionManager.Instance "SocialSecurityNumber", options => { - options.CheckPairDefinitionOnMapping = false; + //Configure options... + }); +```` + +> `options` has a dictionary, named `Configuration` which makes the object extension definitions even extensible. It is used by the EF Core to map extra properties to table fields in the database. See the [extending entities](Customizing-Application-Modules-Extending-Entities.md) document. + +The following sections explain the fundamental property configuration options. + +#### CheckPairDefinitionOnMapping + +Controls how to check property definitions while mapping two extensible objects. See the "Object to Object Mapping" section to understand the `CheckPairDefinitionOnMapping` option better. + +## Validation + +You may want to add some **validation rules** for the extra properties you've defined. `AddOrUpdateProperty` method options allows two ways of performing validation: + +1. You can add **data annotation attributes** for a property. +2. You can write an action (code block) to perform a **custom validation**. + +Validation works when you use the object in a method that is **automatically validated** (e.g. controller actions, page handler methods, application service methods...). So, all extra properties are validated whenever the extended object is being validated. + +### Data Annotation Attributes + +All of the standard data annotation attributes are valid for extra properties. Example: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "SocialSecurityNumber", + options => + { + options.ValidationAttributes.Add(new RequiredAttribute()); + options.ValidationAttributes.Add( + new StringLengthAttribute(32) { + MinimumLength = 6 + } + ); + }); +```` + +With this configuration, `IdentityUserCreateDto` objects will be invalid without a valid `SocialSecurityNumber` value provided. + +### Custom Validation + +If you need, you can add a custom action that is executed to validate the extra properties. Example: + +````csharp +ObjectExtensionManager.Instance + .AddOrUpdateProperty( + "SocialSecurityNumber", + options => + { + options.Validators.Add(context => + { + var socialSecurityNumber = context.Value as string; + + if (socialSecurityNumber == null || + socialSecurityNumber.StartsWith("X")) + { + context.ValidationErrors.Add( + new ValidationResult( + "Invalid social security number: " + socialSecurityNumber, + new[] { "SocialSecurityNumber" } + ) + ); + } + }); }); ```` -> See the "Object to Object Mapping" section to understand the `CheckPairDefinitionOnMapping` option. +`context.ServiceProvider` can be used to resolve a service dependency for advanced scenarios. + +In addition to add custom validation logic for a single property, you can add a custom validation logic that is executed in object level. Example: -`options` has a dictionary, named `Configuration` which makes the object extension definitions even extensible. It is used by the EF Core to map extra properties to table fields in the database. See the [extending entities](Customizing-Application-Modules-Extending-Entities.md) document. +````csharp +ObjectExtensionManager.Instance +.AddOrUpdate(objConfig => +{ + //Define two properties with their own validation rules + + objConfig.AddOrUpdateProperty("Password", propertyConfig => + { + propertyConfig.ValidationAttributes.Add(new RequiredAttribute()); + }); + + objConfig.AddOrUpdateProperty("PasswordRepeat", propertyConfig => + { + propertyConfig.ValidationAttributes.Add(new RequiredAttribute()); + }); + + //Write a common validation logic works on multiple properties + + objConfig.Validators.Add(context => + { + if (context.ValidatingObject.GetProperty("Password") != + context.ValidatingObject.GetProperty("PasswordRepeat")) + { + context.ValidationErrors.Add( + new ValidationResult( + "Please repeat the same password!", + new[] { "Password", "PasswordRepeat" } + ) + ); + } + }); +}); +```` ## Object to Object Mapping From 89cee96c61a5641e5168b2e87c899400f3c1dedd Mon Sep 17 00:00:00 2001 From: berkansasmaz Date: Sat, 11 Apr 2020 22:22:53 +0300 Subject: [PATCH 53/62] Fix document typo --- docs/en/Tutorials/Part-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/Tutorials/Part-2.md b/docs/en/Tutorials/Part-2.md index 08e17e0e3f..a754893114 100644 --- a/docs/en/Tutorials/Part-2.md +++ b/docs/en/Tutorials/Part-2.md @@ -688,7 +688,7 @@ Open `book-list.component.html` file in `books\book-list` folder and replace the * `abp-modal` is a pre-built component to show modals. While you could use another approach to show a modal, `abp-modal` provides additional benefits. * We added `New book` button to the `AbpContentToolbar`. -Open `book-list.component.` file in `books\book-list` folder and replace the content as below: +Open `book-list.component.ts` file in `books\book-list` folder and replace the content as below: ```js import { Component, OnInit } from '@angular/core'; From bd041b8774ab4df45de11077de8cfa8189d6b85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 11 Apr 2020 23:10:30 +0300 Subject: [PATCH 54/62] Added more authorization tests. --- .../MethodInvocationAuthorizationService.cs | 7 +++++-- .../Abp/Authorization/Authorization_Tests.cs | 17 +++++++++++++++ .../TestServices/IMyAuthorizedService1.cs | 1 - .../IMySimpleAuthorizedService.cs | 11 ++++++++++ .../TestServices/MySimpleAuthorizedService.cs | 21 +++++++++++++++++++ 5 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMySimpleAuthorizedService.cs create mode 100644 framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MySimpleAuthorizedService.cs diff --git a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs index 6293c0d034..fb35d142cb 100644 --- a/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs +++ b/framework/src/Volo.Abp.Authorization/Volo/Abp/Authorization/MethodInvocationAuthorizationService.cs @@ -27,8 +27,11 @@ namespace Volo.Abp.Authorization return; } - var authorizationPolicy = await AuthorizationPolicy.CombineAsync(_abpAuthorizationPolicyProvider, - GetAuthorizationDataAttributes(context.Method)); + var authorizationPolicy = await AuthorizationPolicy.CombineAsync( + _abpAuthorizationPolicyProvider, + GetAuthorizationDataAttributes(context.Method) + ); + if (authorizationPolicy == null) { return; diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs index c0f1048d06..66b5f5a2e3 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/Authorization_Tests.cs @@ -9,16 +9,33 @@ namespace Volo.Abp.Authorization public class Authorization_Tests : AuthorizationTestBase { private readonly IMyAuthorizedService1 _myAuthorizedService1; + private readonly IMySimpleAuthorizedService _simpleAuthorizedService; private readonly IMyAuthorizedServiceWithRole _myAuthorizedServiceWithRole; private readonly IPermissionDefinitionManager _permissionDefinitionManager; public Authorization_Tests() { _myAuthorizedService1 = GetRequiredService(); + _simpleAuthorizedService = GetRequiredService(); _myAuthorizedServiceWithRole = GetRequiredService(); _permissionDefinitionManager = GetRequiredService(); } + [Fact] + public async Task Should_Not_Allow_To_Call_Authorized_Method_For_Anonymous_User() + { + await Assert.ThrowsAsync(async () => + { + await _simpleAuthorizedService.ProtectedByClassAsync(); + }); + } + + [Fact] + public async Task Should_Allow_To_Call_Anonymous_Method_For_Anonymous_User() + { + (await _simpleAuthorizedService.AnonymousAsync()).ShouldBe(42); + } + [Fact] public async Task Should_Not_Allow_To_Call_Method_If_Has_No_Permission_ProtectedByClass() { diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs index 4c693993cf..b3841c4fed 100644 --- a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMyAuthorizedService1.cs @@ -11,6 +11,5 @@ namespace Volo.Abp.Authorization.TestServices Task ProtectedByClass(); Task ProtectedByClassAsync(); - } } \ No newline at end of file diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMySimpleAuthorizedService.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMySimpleAuthorizedService.cs new file mode 100644 index 0000000000..4b00cdfbae --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/IMySimpleAuthorizedService.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; + +namespace Volo.Abp.Authorization.TestServices +{ + public interface IMySimpleAuthorizedService + { + Task ProtectedByClassAsync(); + + Task AnonymousAsync(); + } +} \ No newline at end of file diff --git a/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MySimpleAuthorizedService.cs b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MySimpleAuthorizedService.cs new file mode 100644 index 0000000000..41cd4e5b85 --- /dev/null +++ b/framework/test/Volo.Abp.Authorization.Tests/Volo/Abp/Authorization/TestServices/MySimpleAuthorizedService.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authorization; +using Volo.Abp.DependencyInjection; + +namespace Volo.Abp.Authorization.TestServices +{ + [Authorize] + public class MySimpleAuthorizedService : IMySimpleAuthorizedService, ITransientDependency + { + public Task ProtectedByClassAsync() + { + return Task.FromResult(42); + } + + [AllowAnonymous] + public Task AnonymousAsync() + { + return Task.FromResult(42); + } + } +} \ No newline at end of file From c57f3ca5e578161998404110281abc64e362b501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sat, 11 Apr 2020 23:57:02 +0300 Subject: [PATCH 55/62] Refactor EntityHelper.TrySetId method. --- .../Volo/Abp/Domain/Entities/EntityHelper.cs | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs index 5233f2accc..ad082c3fb0 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs @@ -13,6 +13,9 @@ namespace Volo.Abp.Domain.Entities /// public static class EntityHelper { + private static readonly ConcurrentDictionary CachedIdProperties = + new ConcurrentDictionary(); + public static bool IsEntity([NotNull] Type type) { return typeof(IEntity).IsAssignableFrom(type); @@ -98,33 +101,29 @@ namespace Volo.Abp.Domain.Entities return Expression.Lambda>(lambdaBody, lambdaParam); } - private static readonly ConcurrentDictionary CachedPropertyInfo = - new ConcurrentDictionary(); - public static void TrySetId( IEntity entity, Func idFactory, - bool checkForDisableGuidGenerationAttribute = false) + bool checkForDisableIdGenerationAttribute = false) { - var property = CachedPropertyInfo.GetOrAdd( - entity.GetType().FullName + checkForDisableGuidGenerationAttribute, () => + var property = CachedIdProperties.GetOrAdd( + $"{entity.GetType().FullName}-{checkForDisableIdGenerationAttribute}", () => { - var entityType = entity.GetType(); - var idProperty = entityType.GetProperties() - .Where(x => x.GetSetMethod(true) != null) - .FirstOrDefault(x => x.Name == nameof(entity.Id)); + var idProperty = entity + .GetType() + .GetProperties() + .FirstOrDefault(x => x.Name == nameof(entity.Id) && + x.GetSetMethod(true) != null); if (idProperty == null) { return null; } - if (checkForDisableGuidGenerationAttribute) + if (checkForDisableIdGenerationAttribute && + idProperty.IsDefined(typeof(DisableIdGenerationAttribute), true)) { - if (idProperty.IsDefined(typeof(DisableIdGenerationAttribute), true)) - { - return null; - } + return null; } return idProperty; From 9c9653b7b136b559ee1ae977e75d502bde99e4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 12 Apr 2020 00:00:28 +0300 Subject: [PATCH 56/62] DynamicProxyIgnoreTypes should care about thread safety. --- .../Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs index b788acbdad..4a41fc409d 100644 --- a/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs +++ b/framework/src/Volo.Abp.Core/Volo/Abp/DynamicProxy/DynamicProxyIgnoreTypes.cs @@ -18,14 +18,20 @@ namespace Volo.Abp.DynamicProxy public static void Add() { - IgnoredTypes.AddIfNotContains(typeof(T)); + lock (IgnoredTypes) + { + IgnoredTypes.AddIfNotContains(typeof(T)); + } } public static bool Contains(Type type, bool includeDerivedTypes = true) { - return includeDerivedTypes - ? IgnoredTypes.Any(t => t.IsAssignableFrom(type)) - : IgnoredTypes.Contains(type); + lock (IgnoredTypes) + { + return includeDerivedTypes + ? IgnoredTypes.Any(t => t.IsAssignableFrom(type)) + : IgnoredTypes.Contains(type); + } } } } \ No newline at end of file From 1bb9250592774d4a9f1c97e9f61c103871c714ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 12 Apr 2020 13:24:35 +0300 Subject: [PATCH 57/62] Add missing namespace and remove unused ones. --- .../Volo/Abp/EntityFrameworkCore/DatabaseFacadeExtensions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DatabaseFacadeExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DatabaseFacadeExtensions.cs index fbea680be1..55bb9428e8 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DatabaseFacadeExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/DatabaseFacadeExtensions.cs @@ -1,6 +1,5 @@ using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.EntityFrameworkCore.Internal; namespace Volo.Abp.EntityFrameworkCore { From b42a6edd97969de0e724fb3ea6379496b4ddd027 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 12 Apr 2020 13:47:58 +0300 Subject: [PATCH 58/62] Fixed #3521: bundling module wrong if condition. --- .../AspNetCore/Mvc/UI/Bundling/BundlerBase.cs | 52 ++++++++++++------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs index 115029dbe5..3b9954940c 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs @@ -46,38 +46,50 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling private void AddFileToBundle(IBundlerContext context, StringBuilder bundleContentBuilder, string fileName) { - string fileContent = null; + var fileContent = GetFileContentConsideringMinification(context, fileName); + fileContent = ProcessBeforeAddingToTheBundle(context, fileName, fileContent); + bundleContentBuilder.Append(fileContent); + } - if (context.IsMinificationEnabled && !IsMinFile(fileName)) + private string GetFileContentConsideringMinification(IBundlerContext context, string fileName) + { + var isMinFile = IsMinFile(fileName); + if (!context.IsMinificationEnabled || isMinFile) { - var minFileInfo = GetMinFileInfoOrNull(fileName); - if (minFileInfo != null) + var fileContent = GetFileInfo(context, fileName).ReadAsString(); + Logger.LogDebug($"- {fileName} ({fileContent.Length} bytes)"); + if (context.IsMinificationEnabled && isMinFile) { - Logger.LogDebug($"- {fileName} ({minFileInfo.Length} bytes) - already minified"); - fileContent = minFileInfo.ReadAsString(); + Logger.LogDebug(" > Already minified"); } + + return fileContent; } - if (fileContent == null) + var minFileInfo = GetMinFileInfoOrNull(fileName); + if (minFileInfo != null) { - fileContent = GetFileContent(context, fileName); - Logger.LogDebug($"- {fileName} ({fileContent.Length} bytes) - non minified"); - - if (context.IsMinificationEnabled) - { - var nonMinifiedSize = fileContent.Length; - fileContent = Minifier.Minify(fileContent, context.BundleRelativePath); - Logger.LogInformation($" > Minified {fileName} ({nonMinifiedSize} bytes -> {fileContent.Length} bytes)"); - } + var fileContent = minFileInfo.ReadAsString(); + Logger.LogDebug($"- {fileName}"); + Logger.LogDebug($" > Using the pre-minified file: {minFileInfo.Name} ({fileContent.Length} bytes)"); + return fileContent; } - fileContent = ProcessBeforeAddingToTheBundle(context, fileName, fileContent); - bundleContentBuilder.Append(fileContent); + return GetAndMinifyFileContent(context, fileName); } - protected virtual string GetFileContent(IBundlerContext context, string file) + private string GetAndMinifyFileContent(IBundlerContext context, string fileName) { - return GetFileInfo(context, file).ReadAsString(); + var fileContent = GetFileInfo(context, fileName).ReadAsString(); + var nonMinifiedSize = fileContent.Length; + + Logger.LogDebug($"- {fileName} ({nonMinifiedSize} bytes) - non minified, minifying..."); + + fileContent = Minifier.Minify(fileContent, context.BundleRelativePath); + + Logger.LogInformation($" > Minified {fileName} ({nonMinifiedSize} bytes -> {fileContent.Length} bytes)"); + + return fileContent; } protected virtual IFileInfo GetFileInfo(IBundlerContext context, string file) From 2132e406bd50a947862755020285e8f998240586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 12 Apr 2020 13:48:52 +0300 Subject: [PATCH 59/62] Increase log level for Microsoft.EntityFrameworkCore. --- .../aspnet-core/src/MyCompanyName.MyProjectName.Web/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Program.cs b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Program.cs index 5143e37164..b57311cc96 100644 --- a/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Program.cs +++ b/templates/app/aspnet-core/src/MyCompanyName.MyProjectName.Web/Program.cs @@ -17,6 +17,7 @@ namespace MyCompanyName.MyProjectName.Web .MinimumLevel.Information() #endif .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Warning) .Enrich.FromLogContext() .WriteTo.Async(c => c.File("Logs/logs.txt")) .CreateLogger(); From 53d43ccb3d5b661221bf6b7301e959d0f9836488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 12 Apr 2020 14:56:39 +0300 Subject: [PATCH 60/62] Log the original file name on uglifier errors. --- .../AspNetCore/Mvc/UI/Bundling/BundlerBase.cs | 6 +++++- .../Volo/Abp/Minify/IMinifier.cs | 7 ++++++- .../Abp/Minify/NUglify/NUglifyMinifierBase.cs | 18 ++++++++++++++---- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs index 3b9954940c..299e8f834e 100644 --- a/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs +++ b/framework/src/Volo.Abp.AspNetCore.Mvc.UI.Bundling/Volo/Abp/AspNetCore/Mvc/UI/Bundling/BundlerBase.cs @@ -85,7 +85,11 @@ namespace Volo.Abp.AspNetCore.Mvc.UI.Bundling Logger.LogDebug($"- {fileName} ({nonMinifiedSize} bytes) - non minified, minifying..."); - fileContent = Minifier.Minify(fileContent, context.BundleRelativePath); + fileContent = Minifier.Minify( + fileContent, + context.BundleRelativePath, + fileName + ); Logger.LogInformation($" > Minified {fileName} ({nonMinifiedSize} bytes -> {fileContent.Length} bytes)"); diff --git a/framework/src/Volo.Abp.Minify/Volo/Abp/Minify/IMinifier.cs b/framework/src/Volo.Abp.Minify/Volo/Abp/Minify/IMinifier.cs index 6e298d4d82..206640870b 100644 --- a/framework/src/Volo.Abp.Minify/Volo/Abp/Minify/IMinifier.cs +++ b/framework/src/Volo.Abp.Minify/Volo/Abp/Minify/IMinifier.cs @@ -1,7 +1,12 @@ +using JetBrains.Annotations; + namespace Volo.Abp.Minify { public interface IMinifier { - string Minify(string source, string fileName = null); + string Minify( + string source, + [CanBeNull] string fileName = null, + [CanBeNull] string originalFileName = null); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Minify/Volo/Abp/Minify/NUglify/NUglifyMinifierBase.cs b/framework/src/Volo.Abp.Minify/Volo/Abp/Minify/NUglify/NUglifyMinifierBase.cs index 6f52e383a6..491cff1a66 100644 --- a/framework/src/Volo.Abp.Minify/Volo/Abp/Minify/NUglify/NUglifyMinifierBase.cs +++ b/framework/src/Volo.Abp.Minify/Volo/Abp/Minify/NUglify/NUglifyMinifierBase.cs @@ -8,21 +8,31 @@ namespace Volo.Abp.Minify.NUglify { public abstract class NUglifyMinifierBase : IMinifier, ITransientDependency { - private static void CheckErrors(UglifyResult result) + private static void CheckErrors(UglifyResult result, string originalFileName) { if (result.HasErrors) { + var errorMessage = "There are some errors on uglifying the given source code!"; + + if (originalFileName != null) + { + errorMessage += " Original file: " + originalFileName; + } + throw new NUglifyException( - $"There are some errors on uglifying the given source code!{Environment.NewLine}{result.Errors.Select(err => err.ToString()).JoinAsString(Environment.NewLine)}", + $"{errorMessage}{Environment.NewLine}{result.Errors.Select(err => err.ToString()).JoinAsString(Environment.NewLine)}", result.Errors ); } } - public string Minify(string source, string fileName = null) + public string Minify( + string source, + string fileName = null, + string originalFileName = null) { var result = UglifySource(source, fileName); - CheckErrors(result); + CheckErrors(result, originalFileName); return result.Code; } From 26ba303c0292d41057bc499c43167aa683383d08 Mon Sep 17 00:00:00 2001 From: liangshiwei Date: Sun, 12 Apr 2020 21:39:11 +0800 Subject: [PATCH 61/62] Update document --- docs/en/UI/Common/Utils/Linked-List.md | 13 +++++++------ docs/zh-Hans/UI/Common/Utils/Linked-List.md | 14 ++++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/docs/en/UI/Common/Utils/Linked-List.md b/docs/en/UI/Common/Utils/Linked-List.md index c53b3edd06..dd9251813c 100644 --- a/docs/en/UI/Common/Utils/Linked-List.md +++ b/docs/en/UI/Common/Utils/Linked-List.md @@ -1381,10 +1381,9 @@ list.forEach((node, index) => console.log(node.value + index)); ``` - #### \*\[Symbol.iterator\]\(\) -链表是可迭代的. 换句话说你可以使用诸如`for ... of`之类的方法. +A linked list is iterable. In other words, you may use methods like `for...of` on it. ```js list.addTailMany(['a', 'b', 'c']); @@ -1408,7 +1407,7 @@ for(const node of list) { toArray(): T\[\] ``` -转换链表值为数组: +Converts a linked list to an array of values: ```js list.addTailMany(['a', 'b', 'c']); @@ -1430,7 +1429,7 @@ arr === ['a', 'b', 'c'] toNodeArray(): T\[\] ``` -转换链表节点为数组: +Converts a linked list to an array of nodes: ```js list.addTailMany(['a', 'b', 'c']); @@ -1454,7 +1453,7 @@ arr[2].value === 'a' toString(): string ``` -将链表转换为节点及其关系的字符串表示形式: +Converts a linked list to a string representation of nodes and their relations: ```js list.addTailMany(['a', 2, 'c', { k: 4, v: 'd' }]); @@ -1468,7 +1467,9 @@ str === '"a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}' */ ``` -你可以在对值进行字符串化之前通过自定义映射器函数来映射值: + + +You may pass a custom mapper function to map values before stringifying them: ```js list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }, { x: 5 }]).tail(); diff --git a/docs/zh-Hans/UI/Common/Utils/Linked-List.md b/docs/zh-Hans/UI/Common/Utils/Linked-List.md index c468275bba..8dc4d20af9 100644 --- a/docs/zh-Hans/UI/Common/Utils/Linked-List.md +++ b/docs/zh-Hans/UI/Common/Utils/Linked-List.md @@ -1,4 +1,4 @@ -# 链表 (双向)) +# 链表 (双向) Core模块提供了称为[双链表](https://en.wikipedia.org/wiki/Doubly_linked_list)的实用数据结构. 简而言之双向链表是一系列记录(又称节点),这些记录具有上一个节点,下一个节点及其自身值(或数据)的信息. @@ -1352,7 +1352,7 @@ list.forEach((node, index) => console.log(node.value + index)); #### \*\[Symbol.iterator\]\(\) -A linked list is iterable. In other words, you may use methods like `for...of` on it. +链表是可迭代的. 换句话说你可以使用诸如`for ... of`之类的方法. ```js list.addTailMany(['a', 'b', 'c']); @@ -1376,7 +1376,7 @@ for(const node of list) { toArray(): T\[\] ``` -Converts a linked list to an array of values: +转换链表值为数组: ```js list.addTailMany(['a', 'b', 'c']); @@ -1398,7 +1398,7 @@ arr === ['a', 'b', 'c'] toNodeArray(): T\[\] ``` -Converts a linked list to an array of nodes: +转换链表节点为数组: ```js list.addTailMany(['a', 'b', 'c']); @@ -1422,7 +1422,7 @@ arr[2].value === 'a' toString(): string ``` -Converts a linked list to a string representation of nodes and their relations: +将链表转换为节点及其关系的字符串表示形式: ```js list.addTailMany(['a', 2, 'c', { k: 4, v: 'd' }]); @@ -1436,9 +1436,7 @@ str === '"a" <-> 2 <-> "c" <-> {"k":4,"v":"d"}' */ ``` - - -You may pass a custom mapper function to map values before stringifying them: +你可以在对值进行字符串化之前通过自定义映射器函数来映射值: ```js list.addMany([{ x: 1 }, { x: 2 }, { x: 3 }, { x: 4 }, { x: 5 }]).tail(); From 22776050872e62cb68ab8bc4915728cc1dfde1d2 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 12 Apr 2020 21:44:52 +0800 Subject: [PATCH 62/62] Update docs-nav.json --- docs/zh-Hans/docs-nav.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh-Hans/docs-nav.json b/docs/zh-Hans/docs-nav.json index 47e0c1b05e..1a38ae1213 100644 --- a/docs/zh-Hans/docs-nav.json +++ b/docs/zh-Hans/docs-nav.json @@ -315,7 +315,7 @@ ] }, { - "text": "公共", + "text": "通用", "items": [ { "text": "链表 (双向)",