From de46b328d6c5dc4d8fd6bdcb0540d25c71b447a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=B6=9B?= Date: Thu, 18 Apr 2019 13:47:10 +0800 Subject: [PATCH 01/18] Improve Chinese translation for Dependency Injection --- docs/zh-Hans/Dependency-Injection.md | 73 +++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/docs/zh-Hans/Dependency-Injection.md b/docs/zh-Hans/Dependency-Injection.md index 8328c12918..b46c602c4c 100644 --- a/docs/zh-Hans/Dependency-Injection.md +++ b/docs/zh-Hans/Dependency-Injection.md @@ -2,9 +2,11 @@ ABP的依赖注入系统是基于Microsoft的[依赖注入扩展](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection)库(Microsoft.Extensions.DependencyInjection nuget包)开发的.因此,它的文档在ABP中也是有效的. +> 虽然ABP框架没有对任何第三方DI提供程序的核心依赖, 但它必须使用一个提供程序来支持动态代理(dynamic proxying)和一些高级特性以便ABP特性能正常工作.启动模板中已安装了Autofac. 更多信息请参阅 [Autofac 集成](Autofac-Integration.md) 文档. + ### 模块化 -由于ABP是一个模块化框架,因此每个模块都通过依赖注入定义它自己的服务并通过它自己的单独[模块类](Module-Development-Basics.md)进行注册.例: +由于ABP是一个模块化框架,因此每个模块都定义它自己的服务并在它自己的单独[模块类](Module-Development-Basics.md)中通过依赖注入进行注册.例: ````C# public class BlogModule : AbpModule @@ -69,15 +71,15 @@ public class BlogPostAppService : ApplicationService } ```` -``BlogPostAppService`` 由于它是从已知的基类派生的,因此会自动注册transient. +``BlogPostAppService`` 由于它是从已知的基类派生的,因此会自动注册为transient生命周期. #### 依赖接口 如果实现这些接口,则会自动将类注册到依赖注入: -* ``ITransientDependency`` 注册为transient. -* ``ISingletonDependency`` 注册为singleton. -* ``IScopedDependency`` 注册为scoped. +* ``ITransientDependency`` 注册为transient生命周期. +* ``ISingletonDependency`` 注册为singleton生命周期. +* ``IScopedDependency`` 注册为scoped生命周期. 示例: @@ -87,9 +89,9 @@ public class TaxCalculator : ITransientDependency } ```` -``TaxCalculator``因为实现了``ITransientDependency``,所以它会自动注册为transient. +``TaxCalculator``因为实现了``ITransientDependency``,所以它会自动注册为transient生命周期. -#### Dependency 属性 +#### Dependency 特性 配置依赖注入服务的另一种方法是使用``DependencyAttribute``.它具有以下属性: @@ -108,9 +110,9 @@ public class TaxCalculator ```` -``Dependency``如果定义``Lifetime``属性,则具有比其他依赖接口更高的优先级. +如果定义了``Lifetime``属性,则``Dependency``特性具有比其他依赖接口更高的优先级. -#### ExposeServices 属性 +#### ExposeServices 特性 ``ExposeServicesAttribute``用于控制相关类提供了什么服务.例: @@ -133,7 +135,7 @@ public class TaxCalculator: ICalculator, ITaxCalculator, ICanCalculate, ITransie #### 组合到一起 -只要有意义,就可以组合属性和接口. +只要有意义,特性和接口是可以组合在一起使用的. ````C# [Dependency(ReplaceServices = true)] @@ -189,7 +191,7 @@ public class TaxAppService : ApplicationService ``TaxAppService``在构造方法中得到``ITaxCalculator``.依赖注入系统在运行时自动提供所请求的服务. -构造方法注入是将依赖项注入类的优先方式.这样,除非提供了所有构造方法注入的依赖项,否则无法构造类.因此,该类明确的声明了它必需的服务. +构造方法注入是将依赖项注入类的首选方式.这样,除非提供了所有构造方法注入的依赖项,否则无法构造类.因此,该类明确的声明了它必需的服务. #### 属性注入 @@ -266,6 +268,55 @@ using (var scope = _serviceProvider.CreateScope()) 两个服务在创建的scope被处理时(在using块的末尾)释放. +## 高级特性 + +### IServiceCollection.OnRegistred 事件 + +你可能想在注册到依赖注入的每个服务上执行一个操作, 在你的模块的 `PreConfigureServices` 方法中, 使用 `OnRegistred` 方法注册一个回调(callback) , 如下所示: + +````csharp +public class AppModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.OnRegistred(ctx => + { + var type = ctx.ImplementationType; + //... + }); + } +} +```` + +`ImplementationType` 提供了服务类型. 该回调(callback)通常用于向服务添加拦截器. 例如: + +````csharp +public class AppModule : AbpModule +{ + public override void PreConfigureServices(ServiceConfigurationContext context) + { + context.Services.OnRegistred(ctx => + { + if (ctx.ImplementationType.IsDefined(typeof(MyLogAttribute), true)) + { + ctx.Interceptors.TryAdd(); + } + }); + } +} +```` + +这个示例判断一个服务类是否具有 `MyLogAttribute` 特性, 如果有的话就添加一个 `MyLogInterceptor` 到拦截器集合中. + +> 注意, 如果服务类公开了多于一个服务或接口, `OnRegistred` 回调(callback)可能被同一服务类多次调用. 因此, 较安全的方法是使用 `Interceptors.TryAdd` 方法而不是 `Interceptors.Add` 方法. 请参阅动态代理(dynamic proxying)/拦截器 [文档](Dynamic-Proxying-Interceptors.md). + +## 第三方提供程序 + +虽然ABP框架没有对任何第三方DI提供程序的核心依赖, 但它必须使用一个提供程序来支持动态代理(dynamic proxying)和一些高级特性以便ABP特性能正常工作. + +启动模板中已安装了Autofac. 更多信息请参阅 [Autofac 集成](Autofac-Integration.md) 文档. + + ### 请参阅 * [ASP.NET Core依赖注入最佳实践,提示和技巧](https://medium.com/volosoft/asp-net-core-dependency-injection-best-practices-tips-tricks-c6e9c67f9d96) From dc1ad97c5f8313c6f03d3428c779c17b45641ea1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=B6=9B?= Date: Thu, 18 Apr 2019 13:50:40 +0800 Subject: [PATCH 02/18] Fix typo in doc files --- docs/en/Dependency-Injection.md | 2 +- docs/en/Exception-Handling.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/Dependency-Injection.md b/docs/en/Dependency-Injection.md index 5d8ddbf94e..eb0da4a0e4 100644 --- a/docs/en/Dependency-Injection.md +++ b/docs/en/Dependency-Injection.md @@ -96,7 +96,7 @@ public class TaxCalculator : ITransientDependency Another way of configuring a service for dependency injection is to use ``DependencyAttribute``. It has the following properties: * ``Lifetime``: Lifetime of the registration: ``Singleton``, ``Transient`` or ``Scoped``. -* ``TryRegister``: Set ``true`` to register the service only it's not registered before. Uses TryAdd... extension methods of IServiceCollection. +* ``TryRegister``: Set ``true`` to register the service only if it's not registered before. Uses TryAdd... extension methods of IServiceCollection. * ``ReplaceServices``: Set ``true`` to replace services if they are already registered before. Uses Replace extension method of IServiceCollection. Example: diff --git a/docs/en/Exception-Handling.md b/docs/en/Exception-Handling.md index 896f26d723..144c6016dd 100644 --- a/docs/en/Exception-Handling.md +++ b/docs/en/Exception-Handling.md @@ -9,7 +9,7 @@ ABP provides a built-in infrastructure and offers a standard model for handling ### Automatic Exception Handling -`AbpExceptionFilter` handles an exception if **any of the following conditions** are meet: +`AbpExceptionFilter` handles an exception if **any of the following conditions** are met: * Exception is thrown by a **controller action** which returns an **object result** (not a view result). * The request is an AJAX request (`X-Requested-With` HTTP header value is `XMLHttpRequest`). From c623d97cbdc98a0a878a9de61d8fd0b139b0de4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=B6=9B?= Date: Thu, 18 Apr 2019 15:26:46 +0800 Subject: [PATCH 03/18] Improve Chinese translation for Virtual File System and Module Development --- docs/zh-Hans/Module-Development-Basics.md | 10 +++++----- docs/zh-Hans/Virtual-File-System.md | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/zh-Hans/Module-Development-Basics.md b/docs/zh-Hans/Module-Development-Basics.md index 9feaa8c7f9..4519720161 100644 --- a/docs/zh-Hans/Module-Development-Basics.md +++ b/docs/zh-Hans/Module-Development-Basics.md @@ -2,7 +2,7 @@ ### 介绍 -ABP本身是一个模块化框架.它还提供了一个基础架构和架构模型来开发你自己的模块. +ABP本身是一个包含许多nuget包的模块化框架.它还提供了一个完整的基础架构来开发你自己的具有实体, 服务, 数据库集成, API, UI组件等等功能的应用程序模块. ### 模块类 @@ -111,7 +111,7 @@ public class AppModule : AbpModule ### 模块依赖 -在模块化应用程序中,一个模块依赖于另一个模块并不罕见.如果一个Abp模块依赖于另一个模块,它必须声明``[DependsOn]``属性,如下所示: +在模块化应用程序中,一个模块依赖于另一个或几个模块并不罕见.如果一个Abp模块依赖于另一个模块,它必须声明``[DependsOn]``特性,如下所示: ````C# [DependsOn(typeof(AbpAspNetCoreMvcModule))] @@ -122,7 +122,7 @@ public class BlogModule } ```` -你可以根据需要使用多个``DependsOn``属性或将多个模块类型传递给单个``DependsOn``属性. +你可以根据需要使用多个``DependsOn``特性或将多个模块类型传递给单个``DependsOn``特性. 依赖模块可能依赖于另一个模块,但你只需要定义直接依赖项.ABP在启动时会调查应用程序的依赖关系,并以正确的顺序初始化/关闭模块. @@ -130,5 +130,5 @@ public class BlogModule **模块分为两种类型.** 这两种类型并没有任何结构上的区别,只是按功能和用途分类: -- **框架模块**: 这些是**框架的核心模块** 如缓存, 邮件, 主题, 安全, 序列化, 验证, EF Core集成, MongoDB集成... 等. 它们没有应用/业务功能,它们提供了日常开发经常用到的基础设施,集成和抽象. -- **应用程序模块**: 这些模块实现了 **特定的应用/业务功能** 像博客, 文档管理, 身份管理, 租房管理... 等等. 它们通过有自己的实体,服务,API和UI组件. 请参阅 [预构建的应用程序模块](Modules/Index.md). +- **框架模块**: 这些是**框架的核心模块** 如缓存, 邮件, 主题, 安全, 序列化, 验证, EF Core集成, MongoDB集成... 等. 它们没有应用/业务功能,它们提供了日常开发经常用到的通用基础设施,集成和抽象. +- **应用程序模块**: 这些模块实现了 **特定的应用/业务功能** 像博客, 文档管理, 身份管理, 租户管理... 等等. 它们通常有自己的实体,服务,API和UI组件. 请参阅 [预构建的应用程序模块](Modules/Index.md). diff --git a/docs/zh-Hans/Virtual-File-System.md b/docs/zh-Hans/Virtual-File-System.md index 05d8f59a85..5a3e9906b2 100644 --- a/docs/zh-Hans/Virtual-File-System.md +++ b/docs/zh-Hans/Virtual-File-System.md @@ -1,6 +1,6 @@ ## 虚拟文件系统 -虚拟文件系统可以管理文件系统(磁盘)上实际上不存在的文件. 它主要用于将(js, css, image, cshtml ...)文件嵌入到程序集中, 并在运行时将它们用作物理文件. +虚拟文件系统使得管理物理上不存在于文件系统中(磁盘)的文件成为可能. 它主要用于将(js, css, image, cshtml ...)文件嵌入到程序集中, 并在运行时将它们象物理文件一样使用. ### Volo.Abp.VirtualFileSystem nuget包 @@ -30,12 +30,12 @@ namespace MyCompany.MyProject #### 注册嵌入式文件 -首先需要把文件标记为嵌入式资源将文件嵌入到程序集中. 最简单的方式是在 **解决方案管理器** 中选择文件, 然后找到 **"属性"** 窗口将 **"生成操作"** 设置为 **"嵌入式资源"**. +要将文件嵌入到程序集中, 首先需要把该文件标记为嵌入式资源. 最简单的方式是在 **解决方案管理器** 中选择文件, 然后找到 **"属性"** 窗口将 **"生成操作"** 设置为 **"嵌入式资源"**. 例如: ![build-action-embedded-resource-sample](images/build-action-embedded-resource-sample.png) -如果需要添加多个文件, 这会很乏味. 这时可以直接编辑 **.csproj** 文件: +如果需要添加多个文件, 这样做会很乏味. 作为选择, 你可以直接编辑 **.csproj** 文件: ````C# @@ -45,7 +45,7 @@ namespace MyCompany.MyProject 此配置以递归方式添加项目的 **MyResources** 文件夹下的所有文件(包括将来新添加的文件). -然后应该使用 `VirtualFileSystemOptions` 来配置模块, 以便将嵌入式文件注册到虚拟文件系统. 例如: +然后需要使用 `VirtualFileSystemOptions` 来配置模块, 以便将嵌入式文件注册到虚拟文件系统. 例如: ````C# using Microsoft.Extensions.DependencyInjection; @@ -105,9 +105,9 @@ public class MyService #### 在开发过程中处理嵌入式文件 -通过引用程序集(或添加nuget包)将文件嵌入模块程序集并从另一个项目中使用它对于创建可重用模块非常有价值. 但是, 这使得开发模块本身变得有点困难. +将文件嵌入到模块程序集中并能够通过引用程序集(或添加nuget包)在另一个项目中使用它对于创建可重用模块非常有价值. 但是, 这使得开发模块本身变得有点困难. -假设你正在开发一个包含嵌入式JavaScript文件的模块. 当你更改文件时, 你必须重新编译项目, 重新启动应用程序并刷新浏览器页面以进行更改. 显然, 这是非常耗时和乏味的. +假设你正在开发一个包含嵌入式JavaScript文件的模块. 当你更改文件时, 你必须重新编译项目, 重新启动应用程序并刷新浏览器页面以使更改生效. 显然, 这是非常耗时和乏味的. 我们需要的是应用程序在开发时直接使用物理文件的能力, 让浏览器刷新时同步JavaScript文件的任何更改. `ReplaceEmbeddedByPhysical` 方法使其成为可能. @@ -145,7 +145,7 @@ public class MyWebAppModule : AbpModule * 虚拟文件可以像Web应用程序上的物理(静态)文件一样使用. * Razor Views, Razor Pages, js, css, 图像文件和所有其他Web内容可以嵌入到程序集中并像物理文件一样使用. -* 应用程序(或其他模块)可以覆盖模块的虚拟文件, 就像将具有同名和同扩展名文件放入虚拟文件的同一文件夹中一样. +* 应用程序(或其他模块)可以覆盖模块的虚拟文件, 就像将具有相同名称和扩展名的文件放入虚拟文件的同一文件夹中一样. #### 虚拟文件中间件 @@ -155,12 +155,12 @@ public class MyWebAppModule : AbpModule app.UseVirtualFiles(); ```` -在静态文件中间件之后添加虚拟文件中间件, 可以通过放置在同一位置,使用物理文件来覆盖虚拟文件. +在静态文件中间件之后添加虚拟文件中间件, 使得通过在虚拟文件相同的位置放置物理文件, 从而用物理文件覆盖虚拟文件成为可能. -> 虚拟文件中间件只是像静态文件一样提供虚拟wwwroot文件夹内容. +> 虚拟文件中间件只能虚拟wwwroot文件夹中的内容 - 就像静态文件一样. #### Views & Pages -无需任何配置即可在应用程序中使用嵌入式的 razor Views/pages. 只需要将它们放入模块开发中的标准 Views/Pages 虚拟文件夹即可. +无需任何配置即可在应用程序中使用嵌入式的 razor Views/pages. 只需要将它们放置在要开发的模块中的标准 Views/Pages 虚拟文件夹即可. 如果模块/应用程序将新文件放置同一位置, 则会覆盖嵌入式的 Views/Pages. From d690cddb48008e247c19cd6d7ccd108ee69c9c65 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Thu, 18 Apr 2019 13:27:53 +0300 Subject: [PATCH 04/18] FeatureManagement localization --- .../Localization/Resources/FeatureManagement/en.json | 2 +- .../Localization/Resources/FeatureManagement/tr.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/en.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/en.json index 92e4e9582b..68af08b6a1 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/en.json +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/en.json @@ -1,6 +1,6 @@ { "culture": "en", "texts": { - + "Features": "Features" } } \ No newline at end of file diff --git a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/tr.json b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/tr.json index 3f906a3cdc..65a48736cc 100644 --- a/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/tr.json +++ b/modules/feature-management/src/Volo.Abp.FeatureManagement.Web/Localization/Resources/FeatureManagement/tr.json @@ -1,6 +1,6 @@ -{ +{ "culture": "tr", "texts": { - + "Features": "Özellikler" } } \ No newline at end of file From bf9a46fe3096be873d30afba2a4f09f88df4f3e7 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Thu, 18 Apr 2019 13:27:59 +0300 Subject: [PATCH 05/18] Update AbpTenantManagementApplicationModule.cs --- .../Abp/TenantManagement/AbpTenantManagementApplicationModule.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/AbpTenantManagementApplicationModule.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/AbpTenantManagementApplicationModule.cs index 8bed38c5ec..1eb7fdf28e 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/AbpTenantManagementApplicationModule.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/AbpTenantManagementApplicationModule.cs @@ -5,6 +5,7 @@ namespace Volo.Abp.TenantManagement { [DependsOn(typeof(AbpTenantManagementDomainModule))] [DependsOn(typeof(AbpTenantManagementApplicationContractsModule))] + [DependsOn(typeof(AbpAutoMapperModule))] public class AbpTenantManagementApplicationModule : AbpModule { public override void ConfigureServices(ServiceConfigurationContext context) From fc8919ef6a57ac58cd33242f650647811afb1fcf Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Thu, 18 Apr 2019 13:28:40 +0300 Subject: [PATCH 06/18] added ConfigureFullAuditedAggregateRoot --- .../Modeling/AbpEntityTypeBuilderExtensions.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs index dcdcb404e8..7ea94df310 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs @@ -6,6 +6,7 @@ using Newtonsoft.Json; using Volo.Abp.Auditing; using Volo.Abp.Data; using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.MultiTenancy; namespace Volo.Abp.EntityFrameworkCore.Modeling @@ -14,6 +15,7 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling { public static void ConfigureByConvention(this EntityTypeBuilder b) { + b.TryConfigureFullAuditedAggregateRoot(); b.TryConfigureConcurrencyStamp(); b.TryConfigureExtraProperties(); b.TryConfigureMayHaveCreator(); @@ -260,6 +262,22 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling } } + public static void ConfigureFullAuditedAggregateRoot(this EntityTypeBuilder b) + where T : FullAuditedAggregateRoot + { + b.As().TryConfigureFullAuditedAggregateRoot(); + } + + public static void TryConfigureFullAuditedAggregateRoot(this EntityTypeBuilder b) + { + if (b.Metadata.ClrType.IsSubclassOf(typeof(FullAuditedAggregateRoot))) + { + b.As().TryConfigureFullAudited(); + b.As().TryConfigureExtraProperties(); + b.As().TryConfigureConcurrencyStamp(); + } + } + //TODO: Add other interfaces (IAuditedObject...) } } From 381140c32e26b10082cf434537e46af9d69b9da5 Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Thu, 18 Apr 2019 14:02:31 +0300 Subject: [PATCH 07/18] AbpTenantManagement permisson improvements --- ...bpTenantManagementPermissionDefinitionProvider.cs | 1 + .../Localization/ApplicationContracts/en.json | 1 + .../Localization/ApplicationContracts/tr.json | 1 + .../TenantManagement/TenantManagementPermissions.cs | 4 +++- .../Volo/Abp/TenantManagement/TenantAppService.cs | 3 +++ .../AbpTenantManagementWebModule.cs | 1 + .../Pages/TenantManagement/Tenants/Index.js | 12 ++++-------- 7 files changed, 14 insertions(+), 9 deletions(-) diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementPermissionDefinitionProvider.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementPermissionDefinitionProvider.cs index 5a2e2e9d46..980e7ccbbc 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementPermissionDefinitionProvider.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/AbpTenantManagementPermissionDefinitionProvider.cs @@ -16,6 +16,7 @@ namespace Volo.Abp.TenantManagement tenantsPermission.AddChild(TenantManagementPermissions.Tenants.Update, L("Permission:Edit"), multiTenancySide: MultiTenancySides.Host); tenantsPermission.AddChild(TenantManagementPermissions.Tenants.Delete, L("Permission:Delete"), multiTenancySide: MultiTenancySides.Host); tenantsPermission.AddChild(TenantManagementPermissions.Tenants.ManageFeatures, L("Permission:ManageFeatures"), multiTenancySide: MultiTenancySides.Host); + tenantsPermission.AddChild(TenantManagementPermissions.Tenants.ManageConnectionStrings, L("Permission:ManageConnectionStrings"), multiTenancySide: MultiTenancySides.Host); } private static LocalizableString L(string name) diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/en.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/en.json index c153af5e10..5107cd269b 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/en.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/en.json @@ -5,6 +5,7 @@ "Permission:Create": "Create", "Permission:Edit": "Edit", "Permission:Delete": "Delete", + "Permission:ManageConnectionStrings": "Manage connection strings", "Permission:ManageFeatures": "Manage features" } } \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/tr.json b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/tr.json index 8ff9d2c2d5..d745a400c3 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/tr.json +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/Localization/ApplicationContracts/tr.json @@ -5,6 +5,7 @@ "Permission:Create": "Oluşturma", "Permission:Edit": "Düzenleme", "Permission:Delete": "Silme", + "Permission:ManageConnectionStrings": "Bağlantı cümlelerini yönet", "Permission:ManageFeatures": "Özellikleri yönet" } } \ No newline at end of file diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs index 8afdfbe8d5..6bc90271bc 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application.Contracts/Volo/Abp/TenantManagement/TenantManagementPermissions.cs @@ -11,6 +11,7 @@ public const string Update = Default + ".Update"; public const string Delete = Default + ".Delete"; public const string ManageFeatures = Default + ".ManageFeatures"; + public const string ManageConnectionStrings = Default + ".ManageConnectionStrings"; } public static string[] GetAll() @@ -22,7 +23,8 @@ Tenants.Create, Tenants.Update, Tenants.Delete, - Tenants.ManageFeatures + Tenants.ManageFeatures, + Tenants.ManageConnectionStrings }; } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs index fa1571a617..6019b67f1c 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Application/Volo/Abp/TenantManagement/TenantAppService.cs @@ -80,12 +80,14 @@ namespace Volo.Abp.TenantManagement await TenantRepository.DeleteAsync(tenant); } + [Authorize(TenantManagementPermissions.Tenants.ManageConnectionStrings)] public async Task GetDefaultConnectionStringAsync(Guid id) { var tenant = await TenantRepository.GetAsync(id); return tenant?.FindDefaultConnectionString(); } + [Authorize(TenantManagementPermissions.Tenants.ManageConnectionStrings)] public async Task UpdateDefaultConnectionStringAsync(Guid id, string defaultConnectionString) { var tenant = await TenantRepository.GetAsync(id); @@ -93,6 +95,7 @@ namespace Volo.Abp.TenantManagement await TenantRepository.UpdateAsync(tenant); } + [Authorize(TenantManagementPermissions.Tenants.ManageConnectionStrings)] public async Task DeleteDefaultConnectionStringAsync(Guid id) { var tenant = await TenantRepository.GetAsync(id); diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs index 8ea03c99f3..855fa24bb3 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/AbpTenantManagementWebModule.cs @@ -61,6 +61,7 @@ namespace Volo.Abp.TenantManagement.Web options.Conventions.AuthorizePage("/TenantManagement/Tenants/Index", TenantManagementPermissions.Tenants.Default); options.Conventions.AuthorizePage("/TenantManagement/Tenants/CreateModal", TenantManagementPermissions.Tenants.Create); options.Conventions.AuthorizePage("/TenantManagement/Tenants/EditModal", TenantManagementPermissions.Tenants.Update); + options.Conventions.AuthorizePage("/TenantManagement/Tenants/ConnectionStrings", TenantManagementPermissions.Tenants.ManageConnectionStrings); }); } } diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.js b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.js index 09a9cc90c5..18d0cceafc 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.js +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.Web/Pages/TenantManagement/Tenants/Index.js @@ -25,7 +25,7 @@ [ { text: l('Edit'), - visible: function () { return true; }, //TODO: Check permission + visible: abp.auth.isGranted('AbpTenantManagement.Tenants.Update'), action: function (data) { _editModal.open({ id: data.record.id @@ -34,9 +34,7 @@ }, { text: l('ConnectionStrings'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('AbpTenantManagement.Tenants.ManageConnectionStrings'), action: function (data) { _connectionStringsModal.open({ id: data.record.id @@ -45,9 +43,7 @@ }, { text: l('Features'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('AbpTenantManagement.Tenants.ManageFeatures'), action: function (data) { _featuresModal.open({ providerName: 'Tenant', @@ -57,7 +53,7 @@ }, { text: l('Delete'), - visible: function () { return true; }, //TODO: Check permission + visible: abp.auth.isGranted('AbpTenantManagement.Tenants.Delete'), confirmMessage: function (data) { return l('TenantDeletionConfirmationMessage', data.record.name)}, action: function (data) { _tenantAppService From dd408edd1756767d09fed0c808b36d151fcff778 Mon Sep 17 00:00:00 2001 From: maliming Date: Thu, 18 Apr 2019 19:05:28 +0800 Subject: [PATCH 08/18] Update Virtual-File-System.md --- docs/zh-Hans/Virtual-File-System.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh-Hans/Virtual-File-System.md b/docs/zh-Hans/Virtual-File-System.md index 5a3e9906b2..84873e4aa8 100644 --- a/docs/zh-Hans/Virtual-File-System.md +++ b/docs/zh-Hans/Virtual-File-System.md @@ -157,7 +157,7 @@ app.UseVirtualFiles(); 在静态文件中间件之后添加虚拟文件中间件, 使得通过在虚拟文件相同的位置放置物理文件, 从而用物理文件覆盖虚拟文件成为可能. -> 虚拟文件中间件只能虚拟wwwroot文件夹中的内容 - 就像静态文件一样. +> 虚拟文件中间件可以虚拟wwwroot文件夹中的内容 - 就像静态文件一样. #### Views & Pages From afd9bc04cee833015eff087557848465075684cb Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Thu, 18 Apr 2019 14:14:00 +0300 Subject: [PATCH 09/18] Update AbpEntityTypeBuilderExtensions.cs --- .../Modeling/AbpEntityTypeBuilderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs index 7ea94df310..c5b19415ac 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs @@ -263,7 +263,7 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling } public static void ConfigureFullAuditedAggregateRoot(this EntityTypeBuilder b) - where T : FullAuditedAggregateRoot + where T : class { b.As().TryConfigureFullAuditedAggregateRoot(); } From 1d865232e0d8aee6c553926153845893722289ee Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Thu, 18 Apr 2019 14:19:03 +0300 Subject: [PATCH 10/18] ConfigureFullAuditedAggregateRoot --- .../BloggingDbContextModelBuilderExtensions.cs | 12 ++++-------- .../IdentityDbContextModelBuilderExtensions.cs | 4 +--- ...IdentityServerDbContextModelCreatingExtensions.cs | 9 +++------ ...nantManagementDbContextModelCreatingExtensions.cs | 4 +--- 4 files changed, 9 insertions(+), 20 deletions(-) diff --git a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs index 397739263a..425fb58575 100644 --- a/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs +++ b/modules/blogging/src/Volo.Blogging.EntityFrameworkCore/Volo/Blogging/EntityFrameworkCore/BloggingDbContextModelBuilderExtensions.cs @@ -35,8 +35,7 @@ namespace Volo.Blogging.EntityFrameworkCore { b.ToTable(options.TablePrefix + "Blogs", options.Schema); - b.ConfigureFullAudited(); - b.ConfigureExtraProperties(); + b.ConfigureFullAuditedAggregateRoot(); b.Property(x => x.Name).IsRequired().HasMaxLength(BlogConsts.MaxNameLength).HasColumnName(nameof(Blog.Name)); b.Property(x => x.ShortName).IsRequired().HasMaxLength(BlogConsts.MaxShortNameLength).HasColumnName(nameof(Blog.ShortName)); @@ -47,8 +46,7 @@ namespace Volo.Blogging.EntityFrameworkCore { b.ToTable(options.TablePrefix + "Posts", options.Schema); - b.ConfigureFullAudited(); - b.ConfigureExtraProperties(); + b.ConfigureFullAuditedAggregateRoot(); b.Property(x => x.BlogId).HasColumnName(nameof(Post.BlogId)); b.Property(x => x.Title).IsRequired().HasMaxLength(PostConsts.MaxTitleLength).HasColumnName(nameof(Post.Title)); @@ -65,8 +63,7 @@ namespace Volo.Blogging.EntityFrameworkCore { b.ToTable(options.TablePrefix + "Comments", options.Schema); - b.ConfigureFullAudited(); - b.ConfigureExtraProperties(); + b.ConfigureFullAuditedAggregateRoot(); b.Property(x => x.Text).IsRequired().HasMaxLength(CommentConsts.MaxTextLength).HasColumnName(nameof(Comment.Text)); b.Property(x => x.RepliedCommentId).HasColumnName(nameof(Comment.RepliedCommentId)); @@ -80,8 +77,7 @@ namespace Volo.Blogging.EntityFrameworkCore { b.ToTable(options.TablePrefix + "Tags", options.Schema); - b.ConfigureFullAudited(); - b.ConfigureExtraProperties(); + b.ConfigureFullAuditedAggregateRoot(); b.Property(x => x.Name).IsRequired().HasMaxLength(TagConsts.MaxNameLength).HasColumnName(nameof(Tag.Name)); b.Property(x => x.Description).HasMaxLength(TagConsts.MaxDescriptionLength).HasColumnName(nameof(Tag.Description)); diff --git a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs index 0e3fb21688..946db12050 100644 --- a/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs +++ b/modules/identity/src/Volo.Abp.Identity.EntityFrameworkCore/Volo/Abp/Identity/EntityFrameworkCore/IdentityDbContextModelBuilderExtensions.cs @@ -22,9 +22,7 @@ namespace Volo.Abp.Identity.EntityFrameworkCore { b.ToTable(options.TablePrefix + "Users", options.Schema); - b.ConfigureFullAudited(); - b.ConfigureExtraProperties(); - b.ConfigureConcurrencyStamp(); + b.ConfigureFullAuditedAggregateRoot(); b.ConfigureAbpUser(); b.Property(u => u.NormalizedUserName).IsRequired().HasMaxLength(IdentityUserConsts.MaxNormalizedUserNameLength).HasColumnName(nameof(IdentityUser.NormalizedUserName)); diff --git a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs index e98cacd81a..67009a2deb 100644 --- a/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs +++ b/modules/identityserver/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/EntityFrameworkCore/IdentityServerDbContextModelCreatingExtensions.cs @@ -26,8 +26,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore { client.ToTable(tablePrefix + "Clients", schema); - client.ConfigureFullAudited(); - client.ConfigureExtraProperties(); + client.ConfigureFullAuditedAggregateRoot(); client.Property(x => x.ClientId).HasMaxLength(ClientConsts.ClientIdMaxLength).IsRequired(); client.Property(x => x.ProtocolType).HasMaxLength(ClientConsts.ProtocolTypeMaxLength).IsRequired(); @@ -160,8 +159,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore { identityResource.ToTable(tablePrefix + "IdentityResources", schema); - identityResource.ConfigureFullAudited(); - identityResource.ConfigureExtraProperties(); + identityResource.ConfigureFullAuditedAggregateRoot(); identityResource.Property(x => x.Name).HasMaxLength(IdentityResourceConsts.NameMaxLength).IsRequired(); identityResource.Property(x => x.DisplayName).HasMaxLength(IdentityResourceConsts.DisplayNameMaxLength); @@ -183,8 +181,7 @@ namespace Volo.Abp.IdentityServer.EntityFrameworkCore { apiResource.ToTable(tablePrefix + "ApiResources", schema); - apiResource.ConfigureFullAudited(); - apiResource.ConfigureExtraProperties(); + apiResource.ConfigureFullAuditedAggregateRoot(); apiResource.Property(x => x.Name).HasMaxLength(ApiResourceConsts.NameMaxLength).IsRequired(); apiResource.Property(x => x.DisplayName).HasMaxLength(ApiResourceConsts.DisplayNameMaxLength); diff --git a/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs b/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs index 0c5b20a638..c88baf22c5 100644 --- a/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs +++ b/modules/tenant-management/src/Volo.Abp.TenantManagement.EntityFrameworkCore/Volo/Abp/TenantManagement/EntityFrameworkCore/AbpTenantManagementDbContextModelCreatingExtensions.cs @@ -22,9 +22,7 @@ namespace Volo.Abp.TenantManagement.EntityFrameworkCore { b.ToTable(tablePrefix + "Tenants", schema); - b.ConfigureFullAudited(); - b.ConfigureExtraProperties(); - b.ConfigureConcurrencyStamp(); + b.ConfigureFullAuditedAggregateRoot(); b.Property(t => t.Name).IsRequired().HasMaxLength(TenantConsts.MaxNameLength); From 624c3eb4847cdddce4ff6a974243de56eb93d26e Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Thu, 18 Apr 2019 14:59:56 +0300 Subject: [PATCH 11/18] Update AbpEntityTypeBuilderExtensions.cs --- .../Modeling/AbpEntityTypeBuilderExtensions.cs | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs index c5b19415ac..fd7a3d495b 100644 --- a/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs +++ b/framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/Modeling/AbpEntityTypeBuilderExtensions.cs @@ -15,7 +15,6 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling { public static void ConfigureByConvention(this EntityTypeBuilder b) { - b.TryConfigureFullAuditedAggregateRoot(); b.TryConfigureConcurrencyStamp(); b.TryConfigureExtraProperties(); b.TryConfigureMayHaveCreator(); @@ -263,20 +262,13 @@ namespace Volo.Abp.EntityFrameworkCore.Modeling } public static void ConfigureFullAuditedAggregateRoot(this EntityTypeBuilder b) - where T : class + where T : class { - b.As().TryConfigureFullAuditedAggregateRoot(); + b.As().TryConfigureFullAudited(); + b.As().TryConfigureExtraProperties(); + b.As().TryConfigureConcurrencyStamp(); } - public static void TryConfigureFullAuditedAggregateRoot(this EntityTypeBuilder b) - { - if (b.Metadata.ClrType.IsSubclassOf(typeof(FullAuditedAggregateRoot))) - { - b.As().TryConfigureFullAudited(); - b.As().TryConfigureExtraProperties(); - b.As().TryConfigureConcurrencyStamp(); - } - } //TODO: Add other interfaces (IAuditedObject...) } From 1d67b58542c93fffce98fed530f4a1126944226b Mon Sep 17 00:00:00 2001 From: Yunus Emre Kalkan Date: Thu, 18 Apr 2019 15:32:53 +0300 Subject: [PATCH 12/18] permissions in index.js files --- .../src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js | 8 ++------ .../Pages/Docs/Admin/Projects/index.js | 8 ++------ .../Pages/Identity/Roles/index.js | 10 +++------- .../Pages/Identity/Users/index.js | 12 +++--------- 4 files changed, 10 insertions(+), 28 deletions(-) diff --git a/modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js b/modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js index c370f10a67..0d8e8b3641 100644 --- a/modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js +++ b/modules/blogging/src/Volo.Blogging.Web/Pages/Admin/Blogs/index.js @@ -21,9 +21,7 @@ [ { text: l('Edit'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('Blogging.Blog.Update'), action: function (data) { _editModal.open({ blogId: data.record.id @@ -32,9 +30,7 @@ }, { text: l('Delete'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('Blogging.Blog.Delete'), confirmMessage: function (data) { return l('BlogDeletionWarningMessage') }, action: function (data) { volo.blogging.blogs diff --git a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js index 46962ddb83..f1ce283543 100644 --- a/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js +++ b/modules/docs/src/Volo.Docs.Admin.Web/Pages/Docs/Admin/Projects/index.js @@ -29,9 +29,7 @@ [ { text: l('Edit'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('Docs.Admin.Projects.Update'), action: function (data) { _editModal.open({ Id: data.record.id @@ -40,9 +38,7 @@ }, { text: l('Delete'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('Docs.Admin.Projects.Delete'), confirmMessage: function (data) { return l('ProjectDeletionWarningMessage') }, action: function (data) { volo.docs.admin.projectsAdmin diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js index 69351e8d96..476b484128 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Roles/index.js @@ -22,9 +22,7 @@ [ { text: l('Edit'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('AbpIdentity.Roles.Update'), action: function (data) { _editModal.open({ id: data.record.id @@ -33,9 +31,7 @@ }, { text: l('Permissions'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('AbpIdentity.Roles.ManagePermissions'), action: function (data) { _permissionsModal.open({ providerName: 'Role', @@ -46,7 +42,7 @@ { text: l('Delete'), visible: function (data) { - return !data.isStatic; //TODO: Check permission + return !data.isStatic && abp.auth.isGranted('AbpIdentity.Roles.Delete'); //TODO: Check permission }, confirmMessage: function (data) { return l('RoleDeletionConfirmationMessage', data.record.name)}, action: function (data) { diff --git a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/index.js b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/index.js index 2ed43afa74..63e79c4bb8 100644 --- a/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/index.js +++ b/modules/identity/src/Volo.Abp.Identity.Web/Pages/Identity/Users/index.js @@ -24,9 +24,7 @@ [ { text: l('Edit'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('AbpIdentity.Users.Update'), action: function (data) { _editModal.open({ id: data.record.id @@ -35,9 +33,7 @@ }, { text: l('Permissions'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('AbpIdentity.Users.ManagePermissions'), action: function (data) { _permissionsModal.open({ providerName: 'User', @@ -47,9 +43,7 @@ }, { text: l('Delete'), - visible: function () { - return true; //TODO: Check permission - }, + visible: abp.auth.isGranted('AbpIdentity.Users.Delete'), confirmMessage: function (data) { return l('UserDeletionConfirmationMessage', data.record.userName); }, action: function (data) { _identityUserAppService From 59c567eb2c16221ddb1461d2620ebfd9473703e7 Mon Sep 17 00:00:00 2001 From: Rashiduddin Yoldash Date: Thu, 18 Apr 2019 22:47:37 +0300 Subject: [PATCH 13/18] IDistributedCache code documentation. --- .../Volo/Abp/Caching/CacheOptions.cs | 2 + .../Volo/Abp/Caching/DistributedCache.cs | 77 +++++++++++++++++++ .../Volo/Abp/Caching/IDistributedCache.cs | 76 ++++++++++++++++++ 3 files changed, 155 insertions(+) diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheOptions.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheOptions.cs index 70f13c6b18..6be122be8f 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheOptions.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/CacheOptions.cs @@ -10,11 +10,13 @@ namespace Volo.Abp.Caching /// Global Cache entry options. /// public DistributedCacheEntryOptions GlobalCacheEntryOptions { get; set; } + /// /// List of all cache configurators. /// (func argument:Name of cache) /// public List> CacheConfigurators { get; set; } //TODO list item use a configurator interface instead? + public CacheOptions() { CacheConfigurators = new List>(); diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs index 9ec056d4d7..e4d2c70ba5 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/DistributedCache.cs @@ -12,6 +12,10 @@ using Volo.Abp.Threading; namespace Volo.Abp.Caching { + /// + /// Represents a distributed cache of type. + /// + /// The type of cache item being cached. public class DistributedCache : IDistributedCache where TCacheItem : class { @@ -57,6 +61,12 @@ namespace Volo.Abp.Caching SetDefaultOptions(); } + /// + /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The cache item, or null. public virtual TCacheItem Get( string key, bool? hideErrors = null) @@ -88,6 +98,13 @@ namespace Volo.Abp.Caching return Serializer.Deserialize(cachedBytes); } + /// + /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The cache item, or null. public virtual async Task GetAsync( string key, bool? hideErrors = null, @@ -123,6 +140,15 @@ namespace Volo.Abp.Caching return Serializer.Deserialize(cachedBytes); } + /// + /// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item + /// provided by delegate and returns the provided cache item. + /// + /// The key of cached item to be retrieved from the cache. + /// The factory delegate is used to provide the cache item when no cache item is found for the given . + /// The cache options for the factory delegate. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The cache item. public TCacheItem GetOrAdd( string key, Func factory, @@ -150,6 +176,16 @@ namespace Volo.Abp.Caching return value; } + /// + /// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item + /// provided by delegate and returns the provided cache item. + /// + /// The key of cached item to be retrieved from the cache. + /// The factory delegate is used to provide the cache item when no cache item is found for the given . + /// The cache options for the factory delegate. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The cache item. public async Task GetOrAddAsync( string key, Func> factory, @@ -179,6 +215,13 @@ namespace Volo.Abp.Caching return value; } + /// + /// Sets the cache item value for the provided key. + /// + /// The key of cached item to be retrieved from the cache. + /// The cache item value to set in the cache. + /// The cache options for the value. + /// Indicates to throw or hide the exceptions for the distributed cache. public virtual void Set( string key, TCacheItem value, @@ -207,6 +250,15 @@ namespace Volo.Abp.Caching } } + /// + /// Sets the cache item value for the provided key. + /// + /// The key of cached item to be retrieved from the cache. + /// The cache item value to set in the cache. + /// The cache options for the value. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The indicating that the operation is asynchronous. public virtual async Task SetAsync( string key, TCacheItem value, @@ -237,6 +289,11 @@ namespace Volo.Abp.Caching } } + /// + /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. public virtual void Refresh( string key, bool? hideErrors = null) @@ -259,6 +316,13 @@ namespace Volo.Abp.Caching } } + /// + /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The indicating that the operation is asynchronous. public virtual async Task RefreshAsync( string key, bool? hideErrors = null, @@ -282,6 +346,12 @@ namespace Volo.Abp.Caching } } + + /// + /// Removes the cache item for given key from cache. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. public virtual void Remove( string key, bool? hideErrors = null) @@ -303,6 +373,13 @@ namespace Volo.Abp.Caching } } + /// + /// Removes the cache item for given key from cache. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The indicating that the operation is asynchronous. public virtual async Task RemoveAsync( string key, bool? hideErrors = null, diff --git a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs index 1567bfca30..23bd6f4cf2 100644 --- a/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs +++ b/framework/src/Volo.Abp.Caching/Volo/Abp/Caching/IDistributedCache.cs @@ -6,20 +6,46 @@ using Microsoft.Extensions.Caching.Distributed; namespace Volo.Abp.Caching { + /// + /// Represents a distributed cache of type. + /// + /// The type of cache item being cached. public interface IDistributedCache where TCacheItem : class { + /// + /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The cache item, or null. TCacheItem Get( string key, bool? hideErrors = null ); + /// + /// Gets a cache item with the given key. If no cache item is found for the given key then returns null. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The cache item, or null. Task GetAsync( [NotNull] string key, bool? hideErrors = null, CancellationToken token = default ); + /// + /// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item + /// provided by delegate and returns the provided cache item. + /// + /// The key of cached item to be retrieved from the cache. + /// The factory delegate is used to provide the cache item when no cache item is found for the given . + /// The cache options for the factory delegate. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The cache item. TCacheItem GetOrAdd( string key, Func factory, @@ -27,6 +53,16 @@ namespace Volo.Abp.Caching bool? hideErrors = null ); + /// + /// Gets or Adds a cache item with the given key. If no cache item is found for the given key then adds a cache item + /// provided by delegate and returns the provided cache item. + /// + /// The key of cached item to be retrieved from the cache. + /// The factory delegate is used to provide the cache item when no cache item is found for the given . + /// The cache options for the factory delegate. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The cache item. Task GetOrAddAsync( [NotNull] string key, Func> factory, @@ -35,6 +71,13 @@ namespace Volo.Abp.Caching CancellationToken token = default ); + /// + /// Sets the cache item value for the provided key. + /// + /// The key of cached item to be retrieved from the cache. + /// The cache item value to set in the cache. + /// The cache options for the value. + /// Indicates to throw or hide the exceptions for the distributed cache. void Set( string key, TCacheItem value, @@ -42,6 +85,15 @@ namespace Volo.Abp.Caching bool? hideErrors = null ); + /// + /// Sets the cache item value for the provided key. + /// + /// The key of cached item to be retrieved from the cache. + /// The cache item value to set in the cache. + /// The cache options for the value. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The indicating that the operation is asynchronous. Task SetAsync( [NotNull] string key, [NotNull] TCacheItem value, @@ -50,22 +102,46 @@ namespace Volo.Abp.Caching CancellationToken token = default ); + /// + /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. void Refresh( string key, bool? hideErrors = null ); + /// + /// Refreshes the cache value of the given key, and resets its sliding expiration timeout. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The indicating that the operation is asynchronous. Task RefreshAsync( string key, bool? hideErrors = null, CancellationToken token = default ); + /// + /// Removes the cache item for given key from cache. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. void Remove( string key, bool? hideErrors = null ); + /// + /// Removes the cache item for given key from cache. + /// + /// The key of cached item to be retrieved from the cache. + /// Indicates to throw or hide the exceptions for the distributed cache. + /// The for the task. + /// The indicating that the operation is asynchronous. Task RemoveAsync( string key, bool? hideErrors = null, From 2012aa96458713892e06c0361d48c6992bf7b7b6 Mon Sep 17 00:00:00 2001 From: maliming Date: Fri, 19 Apr 2019 10:17:05 +0800 Subject: [PATCH 14/18] Outdated multi-tenant documentation. --- docs/en/Multi-Tenancy.md | 26 -------------------------- docs/zh-Hans/Multi-Tenancy.md | 26 -------------------------- 2 files changed, 52 deletions(-) diff --git a/docs/en/Multi-Tenancy.md b/docs/en/Multi-Tenancy.md index 118fea5c19..ee0648f483 100644 --- a/docs/en/Multi-Tenancy.md +++ b/docs/en/Multi-Tenancy.md @@ -90,32 +90,6 @@ namespace MyCompany.MyProject TODO: ... -### Volo.Abp.MultiTenancy Package - -Volo.Abp.MultiTenancy is the actual package that makes your application multi-tenant. Install it into your project using PMC: - -```` -Install-Package Volo.Abp.MultiTenancy -```` - -Then you can add **AbpMultiTenancyModule** dependency to your module: - -````C# -using Volo.Abp.Modularity; -using Volo.Abp.MultiTenancy; - -namespace MyCompany.MyProject -{ - [DependsOn(typeof(AbpMultiTenancyModule))] - public class MyModule : AbpModule - { - //... - } -} -```` - -> If you add AbpMultiTenancyModule dependency to your module, then you don't need to add AbpMultiTenancyModule dependency separately since AbpMultiTenancyModule already depends on it. - #### Determining Current Tenant The first thing for a multi-tenant application is to determine the current tenant on the runtime. Volo.Abp.MultiTenancy package only provides abstractions (named as tenant resolver) for determining the current tenant, however it does not have any implementation out of the box. diff --git a/docs/zh-Hans/Multi-Tenancy.md b/docs/zh-Hans/Multi-Tenancy.md index 6364428ec1..911803b1be 100644 --- a/docs/zh-Hans/Multi-Tenancy.md +++ b/docs/zh-Hans/Multi-Tenancy.md @@ -90,32 +90,6 @@ namespace MyCompany.MyProject TODO: ... -### Volo.Abp.MultiTenancy - -Volo.Abp.MultiTenancy 才是让你的程序实现多租户的真正的包.使用PMC将它安装到你的项目中: - -```` -Install-Package Volo.Abp.MultiTenancy -```` - -然后添加 **AbpMultiTenancyModule** 依赖到你的模块中: - -````C# -using Volo.Abp.Modularity; -using Volo.Abp.MultiTenancy; - -namespace MyCompany.MyProject -{ - [DependsOn(typeof(AbpMultiTenancyModule))] - public class MyModule : AbpModule - { - //... - } -} -```` - -> 如果你添加了AbpMultiTenancyModule依赖,就不需要再另外添加AbpMultiTenancyModule依赖了,因为AbpMultiTenancyModule已经依赖它了. - #### 确定当前租户 多租户的应用程序运行的时候首先要做的就是确定当前租户. From 01ec2133b87461cea9001ac0570f3bfff6398280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=B6=9B?= Date: Fri, 19 Apr 2019 11:39:56 +0800 Subject: [PATCH 15/18] Improve Entities document --- docs/en/Entities.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/en/Entities.md b/docs/en/Entities.md index a90b95d100..3a946461e7 100644 --- a/docs/en/Entities.md +++ b/docs/en/Entities.md @@ -22,7 +22,7 @@ public class Person : Entity } ``` -> If you do not want derive your entity from the base `Entity` class, you can directly implement `IEntity` interface. +> If you do not want to derive your entity from the base `Entity` class, you can directly implement `IEntity` interface. `Entity` class just defines an `Id` property with the given primary **key type**, which is `int` in the sample above. It can be other types like `string`, `Guid`, `long` or whatever you need. @@ -57,9 +57,9 @@ For the example above, the composite key is composed of `UserId` and `RoleId`. F Entities with composite keys should implement the `GetKeys()` method as shown above. -Notice that you also need to define keys of the entity in your **object-to-relational mapping** (ORM) configuration. +Notice that you also need to define keys of the entity in your **object-relational mapping** (ORM) configuration. -> Composite primary keys has a restriction with repositories. Since it has not known Id property, you can not use `IRepository` for these entities. However, you can always use `IRepository`. See repository documentation (TODO: link) for more. +> Composite primary keys has a restriction with repositories. Since it has not known Id property, you can not use `IRepository` for these entities. However, you can always use `IRepository`. See [repositories documentation](Repositories.md) for more. ### AggregateRoot Class @@ -67,7 +67,7 @@ Notice that you also need to define keys of the entity in your **object-to-relat `AggregateRoot` class extends the `Entity` class. So, it also has an `Id` property by default. -> Notice that ABP creates default repositories only for aggregate roots by default. However, it's possible to include all entities. See repository documentation (TODO: link) for more. +> Notice that ABP creates default repositories only for aggregate roots by default. However, it's possible to include all entities. See [repositories documentation](Repositories.md) for more. ABP does not force you to use aggregate roots, you can in fact use the `Entity` class as defined before. However, if you want to implement DDD and want to create aggregate root classes, there are some best practices you may want to consider: @@ -160,7 +160,7 @@ public class OrderLine : Entity > If you do not want to derive your aggregate root from the base `AggregateRoot` class, you can directly implement the `IAggregateRoot` interface. -`Order` is an **aggregate root** with `Guid` type `Id` property. It has a collection of `OrderLine` entities. `OrderLine` is another entity with a composite primary key (`OrderLine` and ` ProductId`). +`Order` is an **aggregate root** with `Guid` type `Id` property. It has a collection of `OrderLine` entities. `OrderLine` is another entity with a composite primary key (`OrderId` and ` ProductId`). While this example may not implement all the best practices of an aggregate root, it still follows good practices: From 7d74829f0334383e802c17ef2ff8243ab675ccfd Mon Sep 17 00:00:00 2001 From: Halil ibrahim Kalkan Date: Fri, 19 Apr 2019 22:11:54 +0300 Subject: [PATCH 16/18] Implement fluent validation as object validation contributor. --- .../AbpFluentValidationModule.cs | 8 -- .../FluentMethodInvocationValidator.cs | 55 -------- .../FluentObjectValidationContributor.cs | 41 ++++++ .../Abp/Validation/AbpValidationModule.cs | 21 ++- .../Abp/Validation/AbpValidationOptions.cs | 4 +- ...taAnnotationObjectValidationContributor.cs | 122 ++++++++++++++++++ .../Abp/Validation/DataAnnotationValidator.cs | 71 ---------- .../Validation/IDataAnnotationValidator.cs | 12 -- .../IObjectValidationContributor.cs | 7 + .../Volo/Abp/Validation/IObjectValidator.cs | 6 +- .../Abp/Validation/ObjectValidationContext.cs | 20 +++ .../Volo/Abp/Validation/ObjectValidator.cs | 92 ++++--------- .../Abp/Validation/ValidationInterceptor.cs | 33 ++--- ...plicationService_FluentValidation_Tests.cs | 3 +- .../ApplicationService_Validation_Tests.cs | 2 +- 15 files changed, 253 insertions(+), 244 deletions(-) delete mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentMethodInvocationValidator.cs create mode 100644 framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs create mode 100644 framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs delete mode 100644 framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationValidator.cs delete mode 100644 framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IDataAnnotationValidator.cs create mode 100644 framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs create mode 100644 framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidationContext.cs diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationModule.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationModule.cs index bbd5d2c888..ffe6162197 100644 --- a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationModule.cs +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/AbpFluentValidationModule.cs @@ -13,13 +13,5 @@ namespace Volo.Abp.FluentValidation { context.Services.AddConventionalRegistrar(new AbpFluentValidationConventionalRegistrar()); } - - public override void ConfigureServices(ServiceConfigurationContext context) - { - Configure(options => - { - options.MethodValidationContributors.Add(); - }); - } } } diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentMethodInvocationValidator.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentMethodInvocationValidator.cs deleted file mode 100644 index 0868093181..0000000000 --- a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentMethodInvocationValidator.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using FluentValidation; -using Volo.Abp.DependencyInjection; -using Volo.Abp.Validation; - -namespace Volo.Abp.FluentValidation -{ - public class FluentMethodInvocationValidator : IMethodInvocationValidator, ITransientDependency - { - private readonly IServiceProvider _serviceProvider; - - public FluentMethodInvocationValidator( - IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - - public void Validate(MethodInvocationValidationContext context) - { - var validationResult = new AbpValidationResult(); - - foreach (var parameterValue in context.ParameterValues) - { - var serviceType = typeof(IValidator<>).MakeGenericType(parameterValue.GetType()); - var validator = _serviceProvider.GetService(serviceType) as IValidator; - if (validator == null) - { - continue; - } - - var result = validator.Validate(parameterValue); - if (!result.IsValid) - { - validationResult.Errors.AddRange( - result.Errors.Select( - error => - new ValidationResult(error.ErrorMessage) - ) - ); - } - } - - if (validationResult.Errors.Any()) - { - //TODO: How to localize messages? - throw new AbpValidationException( - "Method arguments are not valid! See ValidationErrors for details.", - context.Errors - ); - } - } - } -} diff --git a/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs new file mode 100644 index 0000000000..f926782e2d --- /dev/null +++ b/framework/src/Volo.Abp.FluentValidation/Volo/Abp/FluentValidation/FluentObjectValidationContributor.cs @@ -0,0 +1,41 @@ +using FluentValidation; +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Validation; + +namespace Volo.Abp.FluentValidation +{ + public class FluentObjectValidationContributor : IObjectValidationContributor, ITransientDependency + { + private readonly IServiceProvider _serviceProvider; + + public FluentObjectValidationContributor( + IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public void AddErrors(ObjectValidationContext context) + { + var serviceType = typeof(IValidator<>).MakeGenericType(context.ValidatingObject.GetType()); + var validator = _serviceProvider.GetService(serviceType) as IValidator; + if (validator == null) + { + return; + } + + var result = validator.Validate(context.ValidatingObject); + if (!result.IsValid) + { + context.Errors.AddRange( + result.Errors.Select( + error => + new ValidationResult(error.ErrorMessage) + ) + ); + } + } + } +} 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 f47594a88a..bb2d41f927 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationModule.cs @@ -1,4 +1,6 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.Modularity; namespace Volo.Abp.Validation @@ -8,13 +10,24 @@ namespace Volo.Abp.Validation public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.OnRegistred(ValidationInterceptorRegistrar.RegisterIfNeeded); + AutoAddObjectValidationContributors(context.Services); } - public override void ConfigureServices(ServiceConfigurationContext context) + private static void AutoAddObjectValidationContributors(IServiceCollection services) { - Configure(options => + var contributorTypes = new List(); + + services.OnRegistred(context => + { + if (typeof(IObjectValidationContributor).IsAssignableFrom(context.ImplementationType)) + { + contributorTypes.Add(context.ImplementationType); + } + }); + + services.Configure(options => { - options.MethodValidationContributors.Add(); + options.ObjectValidationContributors.AddIfNotContains(contributorTypes); }); } } diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationOptions.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationOptions.cs index c88449da1a..d9a690181e 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationOptions.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/AbpValidationOptions.cs @@ -8,12 +8,12 @@ namespace Volo.Abp.Validation { public List IgnoredTypes { get; } - public ITypeList MethodValidationContributors { get; set; } + public ITypeList ObjectValidationContributors { get; set; } public AbpValidationOptions() { IgnoredTypes = new List(); - MethodValidationContributors = new TypeList(); + ObjectValidationContributors = new TypeList(); } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs new file mode 100644 index 0000000000..5598fe7827 --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationObjectValidationContributor.cs @@ -0,0 +1,122 @@ +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Reflection; + +namespace Volo.Abp.Validation +{ + public class DataAnnotationObjectValidationContributor : IObjectValidationContributor, ITransientDependency + { + public const int MaxRecursiveParameterValidationDepth = 8; + + protected AbpValidationOptions Options { get; } + + public DataAnnotationObjectValidationContributor(IOptions options) + { + Options = options.Value; + } + + public void AddErrors(ObjectValidationContext context) + { + ValidateObjectRecursively(context.Errors, context.ValidatingObject, currentDepth: 1); + } + + protected virtual void ValidateObjectRecursively(List errors, object validatingObject, int currentDepth) + { + if (currentDepth > MaxRecursiveParameterValidationDepth) + { + return; + } + + if (validatingObject == null) + { + return; + } + + AddErrors(errors, validatingObject); + + //Validate items of enumerable + if (validatingObject is IEnumerable) + { + if (!(validatingObject is IQueryable)) + { + foreach (var item in (validatingObject as IEnumerable)) + { + ValidateObjectRecursively(errors, item, currentDepth + 1); + } + } + + return; + } + + var validatingObjectType = validatingObject.GetType(); + + //Do not recursively validate for primitive objects + if (TypeHelper.IsPrimitiveExtended(validatingObjectType)) + { + return; + } + + if (Options.IgnoredTypes.Any(t => t.IsInstanceOfType(validatingObject))) + { + return; + } + + var properties = TypeDescriptor.GetProperties(validatingObject).Cast(); + foreach (var property in properties) + { + if (property.Attributes.OfType().Any()) + { + continue; + } + + ValidateObjectRecursively(errors, property.GetValue(validatingObject), currentDepth + 1); + } + } + + public void AddErrors(List errors, object validatingObject) + { + var properties = TypeDescriptor.GetProperties(validatingObject).Cast(); + + foreach (var property in properties) + { + AddPropertyErrors(validatingObject, property, errors); + } + + if (validatingObject is IValidatableObject validatableObject) + { + errors.AddRange( + validatableObject.Validate(new ValidationContext(validatableObject)) + ); + } + } + + protected virtual void AddPropertyErrors(object validatingObject, PropertyDescriptor property, List errors) + { + var validationAttributes = property.Attributes.OfType().ToArray(); + if (validationAttributes.IsNullOrEmpty()) + { + return; + } + + var validationContext = new ValidationContext(validatingObject) + { + DisplayName = property.DisplayName, + MemberName = property.Name + }; + + foreach (var attribute in validationAttributes) + { + var result = attribute.GetValidationResult(property.GetValue(validatingObject), validationContext); + if (result != null) + { + errors.Add(result); + } + } + } + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationValidator.cs deleted file mode 100644 index 3333354937..0000000000 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/DataAnnotationValidator.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using Volo.Abp.DependencyInjection; - -namespace Volo.Abp.Validation -{ - public class DataAnnotationValidator : IDataAnnotationValidator, ITransientDependency - { - public void Validate(object validatingObject) - { - var errors = GetErrors(validatingObject); - - if (errors.Any()) - { - throw new AbpValidationException( - "Object state is not valid! See ValidationErrors for details.", - errors - ); - } - } - - /// - /// Gets all errors from properties for DataAnnotations attributes and IValidatableObject interface. - /// - public virtual List GetErrors(object validatingObject) - { - var errors = new List(); - var properties = TypeDescriptor.GetProperties(validatingObject).Cast(); - - foreach (var property in properties) - { - AddPropertyErrors(validatingObject, property, errors); - } - - if (validatingObject is IValidatableObject validatableObject) - { - errors.AddRange( - validatableObject.Validate(new ValidationContext(validatableObject)) - ); - } - - return errors; - } - - protected virtual void AddPropertyErrors(object validatingObject, PropertyDescriptor property, List errors) - { - var validationAttributes = property.Attributes.OfType().ToArray(); - if (validationAttributes.IsNullOrEmpty()) - { - return; - } - - var validationContext = new ValidationContext(validatingObject) - { - DisplayName = property.DisplayName, - MemberName = property.Name - }; - - foreach (var attribute in validationAttributes) - { - var result = attribute.GetValidationResult(property.GetValue(validatingObject), validationContext); - if (result != null) - { - errors.Add(result); - } - } - } - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IDataAnnotationValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IDataAnnotationValidator.cs deleted file mode 100644 index 6b14ff66b6..0000000000 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IDataAnnotationValidator.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; - -namespace Volo.Abp.Validation -{ - public interface IDataAnnotationValidator - { - void Validate(object validatingObject); - - List GetErrors(object validatingObject); - } -} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs new file mode 100644 index 0000000000..ca50901bcd --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidationContributor.cs @@ -0,0 +1,7 @@ +namespace Volo.Abp.Validation +{ + public interface IObjectValidationContributor + { + void AddErrors(ObjectValidationContext context); + } +} \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs index 8388605e62..0ce723bab8 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/IObjectValidator.cs @@ -11,6 +11,10 @@ namespace Volo.Abp.Validation bool allowNull = false ); - List GetErrors(object validatingObject, string name = null, bool allowNull = false); + List GetErrors( + object validatingObject, + string name = null, + bool allowNull = false + ); } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidationContext.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidationContext.cs new file mode 100644 index 0000000000..f2e9cd215c --- /dev/null +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidationContext.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; + +namespace Volo.Abp.Validation +{ + public class ObjectValidationContext + { + [NotNull] + public object ValidatingObject { get; } + + public List Errors { get; } + + public ObjectValidationContext([NotNull] object validatingObject) + { + ValidatingObject = Check.NotNull(validatingObject, nameof(validatingObject)); + Errors = new List(); + } + } +} diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs index 2d92af7221..8ff3998f7e 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ObjectValidator.cs @@ -1,25 +1,21 @@ -using System.Collections; +using Microsoft.Extensions.Options; using System.Collections.Generic; -using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Linq; -using Microsoft.Extensions.Options; +using Microsoft.Extensions.DependencyInjection; using Volo.Abp.DependencyInjection; -using Volo.Abp.Reflection; namespace Volo.Abp.Validation { public class ObjectValidator : IObjectValidator, ITransientDependency { - private const int MaxRecursiveParameterValidationDepth = 8; + protected IHybridServiceScopeFactory ServiceScopeFactory { get; } + protected AbpValidationOptions Options { get; } - private readonly AbpValidationOptions _options; - private readonly IDataAnnotationValidator _dataAnnotationValidator; - - public ObjectValidator(IOptions options, IDataAnnotationValidator dataAnnotationValidator) + public ObjectValidator(IOptions options, IHybridServiceScopeFactory serviceScopeFactory) { - _dataAnnotationValidator = dataAnnotationValidator; - _options = options.Value; + ServiceScopeFactory = serviceScopeFactory; + Options = options.Value; } public virtual void Validate(object validatingObject, string name = null, bool allowNull = false) @@ -37,75 +33,35 @@ namespace Volo.Abp.Validation public virtual List GetErrors(object validatingObject, string name = null, bool allowNull = false) { - var errors = new List(); - - if (validatingObject == null && !allowNull) - { - errors.Add( - name == null - ? new ValidationResult("Given object is null!") - : new ValidationResult(name + " is null!", new[] { name }) - ); - - return errors; - } - - ValidateObjectRecursively(errors, validatingObject, currentDepth: 1); - - return errors; - } - - protected virtual void ValidateObjectRecursively(List errors, object validatingObject, int currentDepth) - { - if (currentDepth > MaxRecursiveParameterValidationDepth) - { - return; - } - if (validatingObject == null) { - return; - } - - errors.AddRange(_dataAnnotationValidator.GetErrors(validatingObject)); - - //Validate items of enumerable - if (validatingObject is IEnumerable) - { - if (!(validatingObject is IQueryable)) + if (allowNull) { - foreach (var item in (validatingObject as IEnumerable)) + return new List(); //TODO: Returning an array would be more performent + } + else + { + return new List { - ValidateObjectRecursively(errors, item, currentDepth + 1); - } + name == null + ? new ValidationResult("Given object is null!") + : new ValidationResult(name + " is null!", new[] {name}) + }; } - - return; } - var validatingObjectType = validatingObject.GetType(); + var context = new ObjectValidationContext(validatingObject); - //Do not recursively validate for primitive objects - if (TypeHelper.IsPrimitiveExtended(validatingObjectType)) + using (var scope = ServiceScopeFactory.CreateScope()) { - return; - } - - if (_options.IgnoredTypes.Any(t => t.IsInstanceOfType(validatingObject))) - { - return; - } - - var properties = TypeDescriptor.GetProperties(validatingObject).Cast(); - foreach (var property in properties) - { - if (property.Attributes.OfType().Any()) + foreach (var contributorType in Options.ObjectValidationContributors) { - continue; + var contributor = (IObjectValidationContributor) scope.ServiceProvider.GetRequiredService(contributorType); + contributor.AddErrors(context); } - - ValidateObjectRecursively(errors, property.GetValue(validatingObject), currentDepth + 1); } + + return context.Errors; } } } \ No newline at end of file diff --git a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs index 3b5d519c0c..c7fedec8db 100644 --- a/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs +++ b/framework/src/Volo.Abp.Validation/Volo/Abp/Validation/ValidationInterceptor.cs @@ -1,7 +1,7 @@ -using System; -using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; +using System; +using System.Threading.Tasks; using Volo.Abp.Aspects; using Volo.Abp.DependencyInjection; using Volo.Abp.DynamicProxy; @@ -10,13 +10,11 @@ namespace Volo.Abp.Validation { public class ValidationInterceptor : AbpInterceptor, ITransientDependency { - private readonly AbpValidationOptions _abpValidationOptions; - private readonly IServiceProvider _serviceProvider; + private readonly IMethodInvocationValidator _methodInvocationValidator; - public ValidationInterceptor(IServiceProvider serviceProvider, IOptions abpValidationOptions) + public ValidationInterceptor(IMethodInvocationValidator methodInvocationValidator) { - _serviceProvider = serviceProvider; - _abpValidationOptions = abpValidationOptions.Value; + _methodInvocationValidator = methodInvocationValidator; } public override void Intercept(IAbpMethodInvocation invocation) @@ -47,18 +45,13 @@ namespace Volo.Abp.Validation protected virtual void Validate(IAbpMethodInvocation invocation) { - foreach (var validationContributor in _abpValidationOptions.MethodValidationContributors) - { - var validator = (IMethodInvocationValidator) _serviceProvider.GetRequiredService(validationContributor); - - validator.Validate( - new MethodInvocationValidationContext( - invocation.TargetObject, - invocation.Method, - invocation.Arguments - ) - ); - } + _methodInvocationValidator.Validate( + new MethodInvocationValidationContext( + invocation.TargetObject, + invocation.Method, + invocation.Arguments + ) + ); } } } 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 467a45a999..0cde05efd4 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 @@ -101,8 +101,7 @@ namespace Volo.Abp.FluentValidation output.ShouldBe("444"); } - - + [DependsOn(typeof(AbpAutofacModule))] [DependsOn(typeof(AbpFluentValidationModule))] public class TestModule : AbpModule 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 3f7b49c8b4..8ff4aa8bfd 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 @@ -16,7 +16,7 @@ namespace Volo.Abp.Validation public ApplicationService_Validation_Tests() { - _myAppService = ServiceProvider.GetRequiredService(); + _myAppService = GetRequiredService(); } protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options) From 6539209faaaf5edecbe08f031b332b20c39478d8 Mon Sep 17 00:00:00 2001 From: maliming Date: Sun, 21 Apr 2019 14:30:57 +0800 Subject: [PATCH 17/18] Update Dependency-Injection.md --- docs/zh-Hans/Dependency-Injection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh-Hans/Dependency-Injection.md b/docs/zh-Hans/Dependency-Injection.md index b46c602c4c..381b77ec35 100644 --- a/docs/zh-Hans/Dependency-Injection.md +++ b/docs/zh-Hans/Dependency-Injection.md @@ -319,4 +319,4 @@ public class AppModule : AbpModule ### 请参阅 -* [ASP.NET Core依赖注入最佳实践,提示和技巧](https://medium.com/volosoft/asp-net-core-dependency-injection-best-practices-tips-tricks-c6e9c67f9d96) +* [ASP.NET Core依赖注入最佳实践,提示和技巧](https://cn.abp.io/blog/Abp/asp-net-core-dependency-injection-best-practices-tips-tricks) From d264ab5f204fbd1fbe02616c50b3250fce1a8f95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E6=B6=9B?= Date: Mon, 22 Apr 2019 10:09:30 +0800 Subject: [PATCH 18/18] Add Chinese localization resource file for administration menu --- .../Abp/Ui/Navigation/Localization/Resource/zh-Hans.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/zh-Hans.json diff --git a/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/zh-Hans.json b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/zh-Hans.json new file mode 100644 index 0000000000..bfac148af4 --- /dev/null +++ b/framework/src/Volo.Abp.UI.Navigation/Volo/Abp/Ui/Navigation/Localization/Resource/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "culture": "zh-Hans", + "texts": { + "Menu:Administration": "管理" + } +} \ No newline at end of file