@ -1,2 +1,2 @@ |
|||||
## abp.op |
## abp.io |
||||
Source code of the official web site: https://abp.io |
Source code of the official web site: https://abp.io |
||||
|
|||||
@ -0,0 +1,174 @@ |
|||||
|
# VoloDocs |
||||
|
|
||||
|
## 什么是VoloDocs? |
||||
|
|
||||
|
VoloDocs是一个跨平台的应用程序,帮助你简单的创建良好的文档与构建开发人员社区. 它借助Github集成简化了软件文档. 使用Github的强大功能进行版本控制,托管文档,并允许用户编译文档. |
||||
|
|
||||
|
## 主要功能 |
||||
|
|
||||
|
- 集成Github从仓库提供文档. |
||||
|
- 支持Markdown/HTML文档格式. |
||||
|
- 支持版本控制 (集成Github release). |
||||
|
- 支持多个项目. |
||||
|
- 允许用户在Github上编辑文档. |
||||
|
- 跨平台,可部署到Windows/Linux/macOS. |
||||
|
|
||||
|
## Github源码仓库 |
||||
|
|
||||
|
它是免费并且开源的. 你可以查看VoloDocs源码,我们也欢迎PR: |
||||
|
|
||||
|
https://github.com/abpframework/abp/tree/master/modules/docs |
||||
|
|
||||
|
## 下载 |
||||
|
|
||||
|
你可以在下面的链接下载VoloDocs release版: |
||||
|
|
||||
|
http://apps.abp.io/VoloDocs/VoloDocs.win-x64.zip - **Windows 64 bit** |
||||
|
|
||||
|
http://apps.abp.io/VoloDocs/VoloDocs.win-x86.zip - **Windows 32 bit** |
||||
|
|
||||
|
http://apps.abp.io/VoloDocs/VoloDocs.osx-x64.zip - **MacOS** |
||||
|
|
||||
|
http://apps.abp.io/VoloDocs/VoloDocs.linux-x64.zip - **Linux** |
||||
|
|
||||
|
上面所有的release都是自包含运行时的部署文件. 包含所有的第三方依赖与.Net Core Runtime,所以你不需要安装任何.Net Core SDK/Runtime. |
||||
|
|
||||
|
## 目录结构 |
||||
|
|
||||
|
解压 `VoloDocs.*.zip` 文件, 你会得到一个 `Web` 文件夹和 `Migrator` 文件夹. `Web` 文件夹中包含网站文件, `Migrator` 文件夹中包含构建数据库的应用程序. 在发布网站之前需要创建数据库或将现有的数据库更新到最新迁移. 如果这是你第一次安装VoloDocs, `Migrator` 会创建一个新数据库. 如果是升级已有的VoloDocs则会将数据库升级到最新的迁移. 使用 `Migrator` 你需要在 `appsettings.json` 文件中更改 `ConnectionString`. 参阅下一节了解如何配置VoloDocs应用程序. |
||||
|
|
||||
|
## 逐步部署 |
||||
|
|
||||
|
- ### 数据库迁移 |
||||
|
|
||||
|
进入 `Migrator` 目录中创建或更新数据库. |
||||
|
|
||||
|
打开 `appsettings.json` 文件设置连接字符串,如果你不清楚如何编写连接字符串,可以查看 https://www.connectionstrings.com/. |
||||
|
|
||||
|
设置连接字符串后运行 `Migrate.bat` 如果是Linux或MacOS运行 `DotNet VoloDocs.Migrator.dll` 命令. |
||||
|
|
||||
|
- ### 配置网站 |
||||
|
|
||||
|
进入 `Web` 目录下的VoloDocs文件夹. 打开 `appsettings.json` 文件设置连接字符串(与 `Migrator` 的 `appsettings.json` 相同). 你也可以为网站设置你想要的 `title`,它会显示在网站的左上角. 做完这些现在可以发布网站了. |
||||
|
|
||||
|
- ### 部署网站 |
||||
|
|
||||
|
在上一步我们创建/更新了数据库(确保数据库链接字符串正确). |
||||
|
|
||||
|
- #### 部署到IIS |
||||
|
|
||||
|
- 移动 `Web` 文件夹到你的 `wwwroot ` 文件夹中. |
||||
|
- 重命名 `Web` 文件夹为 `VoloDocs` (现在我们有了 `C:\inetpub\wwwroot\VoloDocs`). |
||||
|
- 创建网站 `VoloDocs` 会自动创建相同名称的应用程序池. 打开 **应用程序池** 并双击 `VoloDocs` 应用程序池做以下设置 |
||||
|
- **.NET CLR 版本**: `无托管代码` |
||||
|
- **托管管道模式**: `集成` |
||||
|
|
||||
|
 |
||||
|
|
||||
|
|
||||
|
|
||||
|
- 如果发生发生发现错误,表示未在服务器安装托管捆绑包. 请参阅文档 [this document](https://docs.microsoft.com/aspnet/core/host-and-deploy/iis/#install-the-net-core-hosting-bundle) 了解如何安装或[下载托管捆绑包](https://www.microsoft.com/net/permalink/dotnetcore-current-windows-runtime-bundle-installer). |
||||
|
|
||||
|
``` |
||||
|
Handler "aspNetCore" has a bad module "AspNetCoreModuleV2" in its module list using IIS |
||||
|
``` |
||||
|
|
||||
|
- 关于托管VoloDocs的更多信息,请参阅 [在IIS上托管ASP.NET Core应用程序的微软官方文档](https://docs.microsoft.com/zh-cn/aspnet/core/host-and-deploy/iis). |
||||
|
|
||||
|
- #### 部署到Azure |
||||
|
|
||||
|
微软有一篇关于将ASP.NET Core应用程序部署到Azure App Service的文档. 我们建议你此[文档](https://docs.microsoft.com/en-us/azure/app-service/app-service-web-get-started-dotnet). |
||||
|
|
||||
|
- #### 使用命令行运行应用程序 |
||||
|
|
||||
|
你可以从命令行运行应用程序,进入 `VoloDocs\Web` 文件夹并运行 `VoloDocs.Web.exe`,如果是Linux或MacOS运行 `DotNet VoloDocs.Web.dll` 命令. |
||||
|
|
||||
|
- ### 首次运行 |
||||
|
|
||||
|
打开浏览器导航到你的网址. |
||||
|
|
||||
|
首次运行网站,我们需要创建一个项目. |
||||
|
|
||||
|
#### 创建项目 |
||||
|
|
||||
|
跳转到以下网址创建项目 |
||||
|
|
||||
|
- `http://<yourwebsite>/Account/Login?returnUrl=/Docs/Admin/Projects` |
||||
|
|
||||
|
##### 默认凭据 |
||||
|
|
||||
|
使用管理员身份登陆到管理后台,用户名密码如下: |
||||
|
|
||||
|
* **用户名**: `admin` |
||||
|
|
||||
|
* **密码**: `1q2w3E*` |
||||
|
|
||||
|
##### 示例项目的定义 |
||||
|
|
||||
|
以下是一个使用了Github源的示例项目信息. |
||||
|
|
||||
|
配置VoloDocs展示存储在Github仓库中ABP Framework的文档. |
||||
|
|
||||
|
这是ABP Framework文档在Github的链接: |
||||
|
|
||||
|
https://github.com/abpframework/abp/tree/master/docs/zh-Hans |
||||
|
|
||||
|
|
||||
|
|
||||
|
* **名称**: `ABP Framework` |
||||
|
|
||||
|
* **简称**: `abp` |
||||
|
|
||||
|
* **格式**: `markdown` |
||||
|
|
||||
|
* **默认文档名称**: `Index` |
||||
|
|
||||
|
* **文档导航名称**: `docs-nav.json` ([参见示例](https://github.com/abpframework/abp/blob/master/docs/zh-Hans/docs-nav.json)) |
||||
|
|
||||
|
* **最小版本**: *留空* *(隐藏之前的版本)* |
||||
|
|
||||
|
* **主网站网址**: `/` |
||||
|
|
||||
|
* **最新版本分支名称**: 留空 |
||||
|
|
||||
|
* **GitHub链接**: `https://github.com/abpframework/abp/tree/{version}/docs/zh-Hans/` |
||||
|
|
||||
|
* **GitHub访问令牌**: [参见如何创建Github访问令牌](#retrieving-github-access-token) |
||||
|
|
||||
|
* **GitHub用户名称**: [参见如何找到自己的Github用户名称](#learn-your-github-username) |
||||
|
|
||||
|
 |
||||
|
|
||||
|
##### 创建访问令牌 |
||||
|
|
||||
|
在Github页右上角找到 `Settings`,然后在左侧导航中点击 `Developer Settings`,在这里选择 `Personal access tokens`. 点击创建新token,输入名称与描述并勾选repo复选框. 如果已有访问令牌但已经忘记,可以选择[重新生成](https://github.com/settings/tokens/new). |
||||
|
|
||||
|
###### 创建仅授权访问公共仓库的token |
||||
|
|
||||
|
想要仅授权访问公共仓库,仅勾选 `public_repo` 就可以了,然后点击页面底部的 `Generate Token` 按钮完成操作. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
###### 创建可访问私有仓库的token |
||||
|
|
||||
|
想要授权访问私有仓库,勾选 `repo` 下的所有选项. 然后点击页面底部的 `Generate Token` 按钮完成操作. |
||||
|
|
||||
|
 |
||||
|
|
||||
|
###### 查看你的Github用户名称 |
||||
|
|
||||
|
在Github页面右上角点击头像图片,"Signed in as ..." 后面就是你的用户名称 |
||||
|
|
||||
|
 |
||||
|
|
||||
|
|
||||
|
|
||||
|
保存项目后转到网站根地址,你会看到文档已经正常显示. |
||||
|
|
||||
|
`http://<yourwebsite>/documents` |
||||
|
|
||||
|
### 发现问题? |
||||
|
|
||||
|
如果你在安装,使用时遇到问题或者发现bug,请发起issues与我们联系: |
||||
|
|
||||
|
https://github.com/abpframework/abp/issues/new |
||||
@ -0,0 +1,33 @@ |
|||||
|
# 领取驱动设计 |
||||
|
|
||||
|
## 什么是DDD? |
||||
|
|
||||
|
ABP框架提供了**基础设施**使基础**DDD**的开发更易实现. DDD在[维基百科中的定义](https://zh.wikipedia.org/wiki/%E5%9F%9F%E9%A9%B1%E5%8A%A8%E5%BC%80%E5%8F%91)如下: |
||||
|
|
||||
|
> **领域驱动设计(DDD)** 是一种通过将实现连接到持续进化的模型来满足复杂需求的软件开发方法. 领域驱动设计的前提是: |
||||
|
> |
||||
|
> - 把项目的主要重点放在核心领域和领域逻辑上 |
||||
|
> - 把复杂的设计放在领域模型上 |
||||
|
> - 发起技术专家和领域专家之间的创造性协作,以迭代方式完善解决特定领域问题的概念模型 |
||||
|
|
||||
|
### 分层 |
||||
|
|
||||
|
ABP框架遵循DDD原则和模式去实现分层应用程序模型,该模型由四个基本层组成: |
||||
|
|
||||
|
- **表示层**: 为用户提供接口. 使用*应用层*实现与用户交互. |
||||
|
- **应用层**: 表示层与领域层的中介,编排业务对象执行特定的应用程序任务. 使用应用程序逻辑实现用例. |
||||
|
- **领域层**: 包含业务对象以及业务规则. 是应用程序的核心. |
||||
|
- **基础设施层**: 提供通用的技术功能,支持更高的层,主要使用第三方类库. |
||||
|
|
||||
|
## 内容 |
||||
|
|
||||
|
* **领域层** |
||||
|
* [实体与聚合根](Entities.md) |
||||
|
* 值对象 |
||||
|
* [仓储](Repositories.md) |
||||
|
* 领域服务 |
||||
|
* 规约 |
||||
|
* **应用层** |
||||
|
* [应用服务](Application-Services.md) |
||||
|
* [数据传输对象(DTO)](Data-Transfer-Objects.md) |
||||
|
* 工作单元 |
||||
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 153 KiB |
|
After Width: | Height: | Size: 146 KiB |
|
After Width: | Height: | Size: 144 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
@ -1,7 +1,10 @@ |
|||||
namespace Volo.Abp.Domain.Entities.Events.Distributed |
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.Domain.Entities.Events.Distributed |
||||
{ |
{ |
||||
public interface IEntityToEtoMapper |
public interface IEntityToEtoMapper |
||||
{ |
{ |
||||
|
[CanBeNull] |
||||
object Map(object entityObj); |
object Map(object entityObj); |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -0,0 +1,24 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<AssemblyName>Volo.Abp.EntityFrameworkCore.Sqlite</AssemblyName> |
||||
|
<PackageId>Volo.Abp.EntityFrameworkCore.Sqlite</PackageId> |
||||
|
<AssetTargetFallback>$(AssetTargetFallback);portable-net45+win8+wp8+wpa81;</AssetTargetFallback> |
||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.4" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\Volo.Abp.EntityFrameworkCore\Volo.Abp.EntityFrameworkCore.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,25 @@ |
|||||
|
using System; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using Microsoft.EntityFrameworkCore.Infrastructure; |
||||
|
using Volo.Abp.EntityFrameworkCore.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.EntityFrameworkCore |
||||
|
{ |
||||
|
public static class AbpDbContextConfigurationContextSqliteExtensions |
||||
|
{ |
||||
|
public static DbContextOptionsBuilder UseSqlite( |
||||
|
[NotNull] this AbpDbContextConfigurationContext context, |
||||
|
[CanBeNull] Action<SqliteDbContextOptionsBuilder> sqliteOptionsAction = null) |
||||
|
{ |
||||
|
if (context.ExistingConnection != null) |
||||
|
{ |
||||
|
return context.DbContextOptions.UseSqlite(context.ExistingConnection, sqliteOptionsAction); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return context.DbContextOptions.UseSqlite(context.ConnectionString, sqliteOptionsAction); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System; |
||||
|
using JetBrains.Annotations; |
||||
|
using Microsoft.EntityFrameworkCore.Infrastructure; |
||||
|
|
||||
|
namespace Volo.Abp.EntityFrameworkCore |
||||
|
{ |
||||
|
public static class AbpDbContextOptionsSqliteExtensions |
||||
|
{ |
||||
|
public static void UseSqlite( |
||||
|
[NotNull] this AbpDbContextOptions options, |
||||
|
[CanBeNull] Action<SqliteDbContextOptionsBuilder> sqliteOptionsAction = null) |
||||
|
{ |
||||
|
options.Configure(context => |
||||
|
{ |
||||
|
context.UseSqlite(sqliteOptionsAction); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public static void UseSqlite<TDbContext>( |
||||
|
[NotNull] this AbpDbContextOptions options, |
||||
|
[CanBeNull] Action<SqliteDbContextOptionsBuilder> sqliteOptionsAction = null) |
||||
|
where TDbContext : AbpDbContext<TDbContext> |
||||
|
{ |
||||
|
options.Configure<TDbContext>(context => |
||||
|
{ |
||||
|
context.UseSqlite(sqliteOptionsAction); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace Volo.Abp.EntityFrameworkCore.Sqlite |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpEntityFrameworkCoreModule) |
||||
|
)] |
||||
|
public class AbpEntityFrameworkCoreSqliteModule : AbpModule |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -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 |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -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) |
||||
|
) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,6 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"Menu:Administration": "管理" |
||||
|
} |
||||
|
} |
||||
@ -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<AbpValidationOptions> options) |
||||
|
{ |
||||
|
Options = options.Value; |
||||
|
} |
||||
|
|
||||
|
public void AddErrors(ObjectValidationContext context) |
||||
|
{ |
||||
|
ValidateObjectRecursively(context.Errors, context.ValidatingObject, currentDepth: 1); |
||||
|
} |
||||
|
|
||||
|
protected virtual void ValidateObjectRecursively(List<ValidationResult> 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<PropertyDescriptor>(); |
||||
|
foreach (var property in properties) |
||||
|
{ |
||||
|
if (property.Attributes.OfType<DisableValidationAttribute>().Any()) |
||||
|
{ |
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
ValidateObjectRecursively(errors, property.GetValue(validatingObject), currentDepth + 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void AddErrors(List<ValidationResult> errors, object validatingObject) |
||||
|
{ |
||||
|
var properties = TypeDescriptor.GetProperties(validatingObject).Cast<PropertyDescriptor>(); |
||||
|
|
||||
|
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<ValidationResult> errors) |
||||
|
{ |
||||
|
var validationAttributes = property.Attributes.OfType<ValidationAttribute>().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); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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 |
|
||||
); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets all errors from properties for DataAnnotations attributes and IValidatableObject interface.
|
|
||||
/// </summary>
|
|
||||
public virtual List<ValidationResult> GetErrors(object validatingObject) |
|
||||
{ |
|
||||
var errors = new List<ValidationResult>(); |
|
||||
var properties = TypeDescriptor.GetProperties(validatingObject).Cast<PropertyDescriptor>(); |
|
||||
|
|
||||
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<ValidationResult> errors) |
|
||||
{ |
|
||||
var validationAttributes = property.Attributes.OfType<ValidationAttribute>().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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,12 +0,0 @@ |
|||||
using System.Collections.Generic; |
|
||||
using System.ComponentModel.DataAnnotations; |
|
||||
|
|
||||
namespace Volo.Abp.Validation |
|
||||
{ |
|
||||
public interface IDataAnnotationValidator |
|
||||
{ |
|
||||
void Validate(object validatingObject); |
|
||||
|
|
||||
List<ValidationResult> GetErrors(object validatingObject); |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.Validation |
||||
|
{ |
||||
|
public interface IObjectValidationContributor |
||||
|
{ |
||||
|
void AddErrors(ObjectValidationContext context); |
||||
|
} |
||||
|
} |
||||
@ -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<ValidationResult> Errors { get; } |
||||
|
|
||||
|
public ObjectValidationContext([NotNull] object validatingObject) |
||||
|
{ |
||||
|
ValidatingObject = Check.NotNull(validatingObject, nameof(validatingObject)); |
||||
|
Errors = new List<ValidationResult>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||