Browse Source

Merge pull request #591 from cnAbp/Translate

Application-services & EF Core integration document translation
pull/594/head
Halil İbrahim Kalkan 7 years ago
committed by GitHub
parent
commit
f093006626
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      docs/en/Entity-Framework-Core.md
  2. 298
      docs/zh-Hans/Application-Services.md
  3. 6
      docs/zh-Hans/AspNetCore/Bundling-Minification.md
  4. 2
      docs/zh-Hans/AspNetCore/Client-Side-Package-Management.md
  5. 8
      docs/zh-Hans/Best-Practices/Application-Services.md
  6. 232
      docs/zh-Hans/Entity-Framework-Core.md
  7. 2
      docs/zh-Hans/Exception-Handling.md
  8. 2
      docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md
  9. 4
      docs/zh-Hans/Getting-Started-Console-Application.md
  10. 4
      docs/zh-Hans/Localization.md
  11. 2
      docs/zh-Hans/Repositories.md
  12. 2
      docs/zh-Hans/Virtual-File-System.md

12
docs/en/Entity-Framework-Core.md

@ -6,7 +6,7 @@ This document explains how to integrate EF Core as an ORM provider to ABP based
`Volo.Abp.EntityFrameworkCore` is the main nuget package for the EF Core integration. Install it to your project (for a layered application, to your data/infrastructure layer):
````
```` shell
Install-Package Volo.Abp.EntityFrameworkCore
````
@ -164,7 +164,7 @@ public class BookRepository : EfCoreRepository<BookStoreDbContext, Book, Guid>,
public async Task DeleteBooksByType(BookType type)
{
await DbContext.Database.ExecuteSqlCommandAsync(
await DbContext.Database.ExecuteSqlCommandAsync(
$"DELETE FROM Books WHERE Type = {(int)type}"
);
}
@ -191,8 +191,8 @@ This is especially important when you want to **override a base repository metho
````csharp
public override async Task DeleteAsync(
Guid id,
bool autoSave = false,
Guid id,
bool autoSave = false,
CancellationToken cancellationToken = default)
{
//TODO: Custom implementation of the delete method
@ -235,8 +235,8 @@ First, define your repository classes like that:
```csharp
public class MyRepositoryBase<TEntity>
: EfCoreRepository<BookStoreDbContext, TEntity>
where TEntity : class, IEntity
: EfCoreRepository<BookStoreDbContext, TEntity>
where TEntity : class, IEntity
{
public MyRepositoryBase(IDbContextProvider<BookStoreDbContext> dbContextProvider)
: base(dbContextProvider)

298
docs/zh-Hans/Application-Services.md

@ -1,3 +1,297 @@
## Application Services
## 应用服务
应用服务实现应用程序的**用例**, 将**领域层逻辑公开给表示层**.
从表示层(可选)调用应用服务,**DTO (数据传对象)** 作为参数. 返回(可选)DTO给表示层.
## 示例
### 图书实体
假设你有一个`Book`实体(聚合根), 如下所示:
````csharp
public class Book : AggregateRoot<Guid>
{
public const int MaxNameLength = 128;
public virtual string Name { get; protected set; }
public virtual BookType Type { get; set; }
public virtual float? Price { get; set; }
protected Book()
{
}
public Book(Guid id, [NotNull] string name, BookType type, float? price = 0)
{
Id = id;
Name = CheckName(name);
Type = type;
Price = price;
}
public virtual void ChangeName([NotNull] string name)
{
Name = CheckName(name);
}
private static string CheckName(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException($"name can not be empty or white space!");
}
if (name.Length > MaxNameLength)
{
throw new ArgumentException($"name can not be longer than {MaxNameLength} chars!");
}
return name;
}
}
````
* `Book`实体中定义`MaxNameLength`限制`Name`属性的最大长度.
* `Book`构造函数与`ChangeName`确保`Name`属性值的有效性. 请注意, `Name`的setter不是`public`.
ABP不会强制开发者这样设计实体, 可以将所有的属性设置Public set/get. 由你来决定是否全面实施DDD.
### IBookAppService接口
在ABP中应用程序服务应该实现`IApplicationService接口`. 推荐每个应用程序服务创建一个接口:
````csharp
public interface IBookAppService : IApplicationService
{
Task CreateAsync(CreateBookDto input);
}
````
我们将实现Create方法作为示例. CreateBookDto定义如下:
```csharp
public class CreateBookDto
{
[Required]
[StringLength(Book.MaxNameLength)]
public string Name { get; set; }
public BookType Type { get; set; }
public float? Price { get; set; }
}
```
有关DTO更的教程,请参见[数据传输对象文档](Entities.md)
### BookAppService(实现)
````csharp
public class BookAppService : ApplicationService, IBookAppService
{
private readonly IRepository<Book, Guid> _bookRepository;
public BookAppService(IRepository<Book, Guid> bookRepository)
{
_bookRepository = bookRepository;
}
public async Task CreateAsync(CreateBookDto input)
{
var book = new Book(
GuidGenerator.Create(),
input.Name,
input.Type,
input.Price
);
await _bookRepository.InsertAsync(book);
}
}
````
* `BookAppService`继承了基类`ApplicationService`· 这不是必需的, 但是`ApplicationService`提供了应用服务常见的需求(比如本示例服务中使用的`GuidGenerator`). 如果不继承它, 我们需要在服务中手动注入`IGuidGenerator`(参见[Guid生成](Guid-Generation.md)文档)
* `BookAppService`按照预期实现了`IBookAppService`
* `BookAppService` 注入了 `IRepository<Book, Guid>`(请参见[仓储](Repositories.md))在CreateAsync方法内部使用仓储将新实体插入数据库.
* `CreateAsync`使用`Book`实体的构造函数从给定的Input值创建新的`Book`对象
### 数据传输对象
应用服务使用并返回DTO而不是实体. ABP不会强制执行此规则. 但是将实体暴露给表示层(或远程客户端)存在重大问题, 所以不建议返回实体.
有关更多信息, 请参见[DTO文档](Entities.md).
### 对象到对象映射
`CreateBook`方法使用参数`CreateBookDto`对象手动创建`Book`实体. 因为`Book`实体的构造函数强制执行(我们是这样设计的).
但是在很多情况下使用**自动对象映射**从相似对象设置对象的属性更加方便实用. ABP提供了一个[对象到对象映射](Object-To-Object-Mapping.md)基础设施,使其变得更加容易.
让我们创建另一种获取`Book`的方法. 首先,在`IBookAppService`接口中定义方法:
````csharp
public interface IBookAppService : IApplicationService
{
Task CreateAsync(CreateBookDto input);
Task<BookDto> GetAsync(Guid id); //New method
}
````
`BookDto`是一个简单的[DTO](Data-Transfer-Objects.md)类, 定义如下:
````csharp
[AutoMapFrom(typeof(Book))] //Defines the mapping
public class BookDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public BookType Type { get; set; }
public float? Price { get; set; }
}
````
* `BookDto`定义了`[AutoMapFrom(typeof(Book))]`属性来从创建对象映射Book到BookDto.
然后你可以实现`GetAsync`方法. 如下所示:
````csharp
public async Task<BookDto> GetAsync(Guid id)
{
var book = await _bookRepository.GetAsync(id);
return book.MapTo<BookDto>();
}
````
`MapTo`扩展方法通过复制具有相同命名的所有属性将`Book`对象转换为`BookDto`对象.
`MapTo`的另一种替代方法是使用`IObjectMapper`服务:
````csharp
public async Task<BookDto> GetAsync(Guid id)
{
var book = await _bookRepository.GetAsync(id);
return ObjectMapper.Map<Book, BookDto>(book);
}
````
虽然第二种语法编写起来有点困难,但是如果你编写单元测试,它会更好地工作.
有关更多信息,请参阅[对象到对象映射](Object-To-Object-Mapping)文档.
### 验证
自动验证应用服务方法的输入(如ASP.NET Core 控制器的actions). 你可以使用标准数据注释属性或自定义验证方法来执行验证. ABP还确保输入不为空.
请参阅[验证](Validation.md)文档了解更多信息.
### 授权
可以对应用程序服务方法使用声明性和命令式授权.
请参阅[授权](Authorization.md)文档了解更多信息.
### CRUD应用服务
如果需要创建具有Create,Update,Delete和Get方法的简单CRUD应用服务,则可以使用ABP的基类轻松构建服务. 你可以继承CrudAppService或 AsyncCrudAppService.
示例:
创建继承`IAsyncCrudAppService`接口的`IBookAppService`接口.
````csharp
public interface IBookAppService :
IAsyncCrudAppService< //Defines CRUD methods
BookDto, //Used to show books
Guid, //Primary key of the book entity
PagedAndSortedResultRequestDto, //Used for paging/sorting on getting a list of books
CreateUpdateBookDto, //Used to create a new book
CreateUpdateBookDto> //Used to update a book
{
}
````
* IAsyncCrudAppService有泛型参数来获取实体的主键类型和CRUD操作的DTO类型(它不获取实体类型,因为实体类型未向客户端公开使用此接口).
`IAsyncCrudAppService`声明以下方法:
````csharp
public interface IAsyncCrudAppService<
TEntityDto,
in TKey,
in TGetListInput,
in TCreateInput,
in TUpdateInput>
: IApplicationService
where TEntityDto : IEntityDto<TKey>
{
Task<TEntityDto> GetAsync(TKey id);
Task<PagedResultDto<TEntityDto>> GetListAsync(TGetListInput input);
Task<TEntityDto> CreateAsync(TCreateInput input);
Task<TEntityDto> UpdateAsync(TKey id, TUpdateInput input);
Task DeleteAsync(TKey id);
}
````
示例中使用的DTO类是`BookDto`和`CreateUpdateBookDto`:
````csharp
[AutoMapFrom(typeof(Book))]
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
public BookType Type { get; set; }
public float Price { get; set; }
}
[AutoMapTo(typeof(Book))]
public class CreateUpdateBookDto
{
[Required]
[StringLength(128)]
public string Name { get; set; }
[Required]
public BookType Type { get; set; } = BookType.Undefined;
[Required]
public float Price { get; set; }
}
````
* `CreateUpdateBookDto`由创建和更新操作共享,但你也可以使用单独的DTO类.
最后`BookAppService`实现非常简单:
````csharp
public class BookAppService :
AsyncCrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
{
}
}
````
`AsyncCrudAppService`实现了`IAsyncCrudAppService`接口中声明的所有方法. 然后,你可以添加自己的自定义方法或覆盖和自定义实现.
### 生命周期
应用服务的生命周期是[transient](Dependency-Injection)的,它们会自动注册到依赖注入系统.
TODO

6
docs/zh-Hans/AspNetCore/Bundling-Minification.md

@ -13,7 +13,7 @@ ABP内置了简单,动态,强大,模块化的方式.
> 默认情况下已在启动模板安装此软件包. 大多数情况下,你不需要手动安装它.
将`Volo.Abp.AspNetCore.Mvc.UI.Bundling` nuget包安装到的项目中:
将`Volo.Abp.AspNetCore.Mvc.UI.Bundling` nuget包安装到的项目中:
````
install-package Volo.Abp.AspNetCore.Mvc.UI.Bundling
@ -132,7 +132,7 @@ public class MyWebModule : AbpModule
}
````
> 可以在脚本和样式包中使用相同的名称(*MyGlobalBundle*), 因为它们被添加到不同的集合(`ScriptBundles`和`StyleBundles`).
> 可以在脚本和样式包中使用相同的名称(*MyGlobalBundle*), 因为它们被添加到不同的集合(`ScriptBundles`和`StyleBundles`).
在定义bundle之后, 可以使用上面定义的相同tag helpers将其包括在页面中. 例如:
@ -311,7 +311,7 @@ services.Configure<BundlingOptions>(options =>
建议为应用程序定义多个包, 每个包用于不同的目的.
* **全局包**: 应用程序中的每个页面都包含全局样式/脚本包. 主题已经定义了全局样式和脚本包. 的模块可以为他们做出贡献.
* **全局包**: 应用程序中的每个页面都包含全局样式/脚本包. 主题已经定义了全局样式和脚本包. 的模块可以为他们做出贡献.
* **布局包**: 这是针对单个布局的特定包. 仅包含在所有页面之间共享的资源使用布局. 使用bundling tag helpers创建捆绑包是一种很好的做法.
* **模块包**: 用于单个模块页面之间的共享资源.
* **页面包**: 为每个页面创建的特定包. 使用bundling tag helpers创建捆绑包作为最佳实践.

2
docs/zh-Hans/AspNetCore/Client-Side-Package-Management.md

@ -45,7 +45,7 @@ yarn
#### 贡献包
如果你需要不在标准软件包中的第三方NPM软件包,可以在Github[repository](https://github.com/volosoft/abp)上创建Pull请求. 接受遵循这些规则的拉取请求:
如果你需要不在标准软件包中的第三方NPM软件包,可以在Github[repository](https://github.com/volosoft/abp)上创建Pull请求. 接受遵循这些规则的拉取请求:
* 对于NPM上的`package-name`, 包名称应该命名为`@abp/package-name`(例如:`bootstrap`包的`@abp/bootstrap`).
* 它应该是**最新的稳定**版本的包.

8
docs/zh-Hans/Best-Practices/Application-Services.md

@ -86,7 +86,7 @@ public class LabelDto : EntityDto<Guid>
* **推荐** 为异步方法使用 **Async** 后缀.
* **不推荐** 在方法名中重复实体的名称.
* 例如: 在 `IProductAppService` 中定义`GetProductAsync(...)` 而不是 `GetAsync(...)` .
* 例如: 在 `IProductAppService` 中定义`GetAsync(...)` 而不是 `GetProductAsync(...)` .
##### 获取单一实体
@ -104,7 +104,7 @@ Task<QuestionWithDetailsDto> GetAsync(Guid id);
* **推荐** 如果需要获取单个DTO可以使用参数进行 **过滤**, **排序****分页**.
* **推荐** 尽可能让过滤参数可选.
* **推荐** 将排序与分页属性设置为可选, 并且提供默认值.
* **推荐** 限制最大页数大小 (基性能考虑).
* **推荐** 限制最大页数大小 (基性能考虑).
* **推荐** 返回 **详细DTO**集合. 示例:
````C#
@ -116,7 +116,7 @@ Task<List<QuestionWithDetailsDto>> GetListAsync(QuestionListQueryDto queryDto);
* **推荐** 使用 `CreateAsync` 做为**方法名**.
* **推荐** 使用**专门的输入DTO**来创建实体.
* **推荐** 使用 **data annotations** 进行输入验证.
* 尽可能在**领域**之间共享常量(通过**domain shared** package定义的常量).
* 尽可能在**领域**之间共享常量(通过**domain shared** package定义的常量).
* **推荐** 只需要创建实体的**最少**信息, 但是提供了其他可选属性.
示例**方法**:
@ -147,7 +147,7 @@ public class CreateQuestionDto
- **推荐** 使用**专门的输入DTO**来更新实体.
- **推荐** 获取实体的id做为分离的原始参数. 不要包含更新DTO.
- **推荐** 使用 **data annotations** 进行输入验证.
- 尽可能在**领域**之间共享常量(通过**domain shared** package定义的常量).
- 尽可能在**领域**之间共享常量(通过**domain shared** package定义的常量).
- **推荐** 返回更新实体的**详细**DTO.
示例:

232
docs/zh-Hans/Entity-Framework-Core.md

@ -4,13 +4,13 @@
### 安装
`Volo.Abp.EntityFrameworkCore` 是EF Core 集成的主要nuget包. 将其安装到你的项目中(在分层应用程序中适用于 数据/基础设施层):
`Volo.Abp.EntityFrameworkCore` 是EF Core 集成的主要nuget包. 将其安装到你的项目中(在分层应用程序中适用于 数据访问/基础设施层):
```
```shell
Install-Package Volo.Abp.EntityFrameworkCore
```
然后添加 `AbpEntityFrameworkCoreModule` 模块依赖项到 [module](Module-Development-Basics.cn.md)(项目中的Mudole类):
然后添加 `AbpEntityFrameworkCoreModule` 模块依赖项(`DependsOn` Attribute) 到 [module](Module-Development-Basics.cn.md)(项目中的Mudole类):
````C#
using Volo.Abp.EntityFrameworkCore;
@ -38,7 +38,7 @@ namespace MyCompany.MyProject
{
public class MyDbContext : AbpDbContext<MyDbContext>
{
//...your DbSet properties
//...在这里添加 DbSet properties
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
@ -74,7 +74,7 @@ namespace MyCompany.MyProject
#### 添加默认仓储
ABP会自动为DbContext中的实体创建仓储 (TODO: link). 需要在注册的时使用`AddDefaultRepositories()`:
ABP会自动为DbContext中的实体创建[默认仓储](Repositories.md). 需要在注册的时使用options添加`AddDefaultRepositories()`:
````C#
services.AddAbpDbContext<MyDbContext>(options =>
@ -83,8 +83,8 @@ services.AddAbpDbContext<MyDbContext>(options =>
});
````
默认情况下为每个聚合根实体(从聚合体派生的类)创建一个仓储. 如果想要为其他实体也创建仓储
需要把`includeAllEntities` 设置为 `true`:
默认情况下为每个[聚合根实体](Entities.md)(`AggregateRoot`派生的子类)创建一个仓储. 如果想要为其他实体也创建仓储
请将`includeAllEntities` 设置为 `true`:
````C#
services.AddAbpDbContext<MyDbContext>(options =>
@ -95,14 +95,226 @@ services.AddAbpDbContext<MyDbContext>(options =>
然后你就可以在服务中注入和使用 `IRepository<TEntity>``IQueryableRepository<TEntity>`.
假如你有一个主键是Guid名为Book实体(聚合根)
```csharp
public class Book : AggregateRoot<Guid>
{
public string Name { get; set; }
public BookType Type { get; set; }
}
```
在[领域服务](Domain-Services.md)中创建一个新的Book实例并且使用仓储持久化到数据库中
````csharp
public class BookManager : DomainService
{
private readonly IRepository<Book, Guid> _bookRepository;
public BookManager(IRepository<Book, Guid> bookRepository) //注入默认仓储
{
_bookRepository = bookRepository;
}
public async Task<Book> CreateBook(string name, BookType type)
{
Check.NotNullOrWhiteSpace(name, nameof(name));
var book = new Book
{
Id = GuidGenerator.Create(),
Name = name,
Type = type
};
await _bookRepository.InsertAsync(book); //使用仓储提供的标准方法
return book;
}
}
````
在这个示例中使用 `InsertAsync` 将新实例插入到数据库中
#### 添加自定义仓储
TODO ...
默认通用仓储可以满足大多数情况下的需求(它实现了`IQueryable`),但是你可能会需要自定义仓储与仓储方法.
假设你需要根据图书类型删除所有的书籍. 建议为自定义仓储定义一个接口:
````csharp
public interface IBookRepository : IRepository<Book, Guid>
{
Task DeleteBooksByType(BookType type);
}
````
你通常希望从IRepository派生以继承标准存储库方法. 然而,你没有必要这样做. 仓储接口在分层应用程序的领域层中定义,它在数据访问/基础设施层([启动模板](https://cn.abp.io/Templates)中的`EntityFrameworkCore`项目)中实现
IBookRepository接口的实现示例:
````csharp
public class BookRepository : EfCoreRepository<BookStoreDbContext, Book, Guid>, IBookRepository
{
public BookRepository(IDbContextProvider<BookStoreDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
public async Task DeleteBooksByType(BookType type)
{
await DbContext.Database.ExecuteSqlCommandAsync(
$"DELETE FROM Books WHERE Type = {(int)type}"
);
}
}
````
现在可以在需要时[注入](Dependency-Injection.md)`IBookRepository`并使用`DeleteBooksByType`方法.
##### 覆盖默认通用仓储
即使创建了自定义仓储,仍可以注入使用默认通用仓储(在本例中是 `IRepository<Book, Guid>`). 默认仓储实现不会使用你创建的自定义仓储类.
如果要将默认仓储实现替换为自定义仓储,请在`AddAbpDbContext`使用options执行:
````csharp
context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
{
options.AddDefaultRepositories();
options.AddRepository<Book, BookRepository>();
});
````
在你想要覆盖默认仓储方法对其自定义时,这一点非常需要. 例如你可能希望自定义`DeleteAsync`方法覆盖默认实现
````csharp
public override async Task DeleteAsync(
Guid id,
bool autoSave = false,
CancellationToken cancellationToken = default)
{
//TODO: Custom implementation of the delete method
}
````
#### 访问 EF Core API
大多数情况下应该隐藏仓储后面的EF Core API(这也是仓储的设计目地). 但是如果想要通过仓储访问DbContext实现,则可以使用`GetDbContext()`或`GetDbSet()`扩展方法. 例:
````csharp
public class BookService
{
private readonly IRepository<Book, Guid> _bookRepository;
public BookService(IRepository<Book, Guid> bookRepository)
{
_bookRepository = bookRepository;
}
public void Foo()
{
DbContext dbContext = _bookRepository.GetDbContext();
DbSet<Book> books = _bookRepository.GetDbSet();
}
}
````
* `GetDbContext` 返回 `DbContext` 引用,而不是 `BookStoreDbContext`. 你可以释放它, 但大多数情况下你不会需要它.
> 要点: 你必须在使用`DbContext`的项目里引用`Volo.Abp.EntityFrameworkCore`包. 这会破坏封装,但在这种情况下,这就是你需要的.
#### 高级主题
##### 设置默认仓储类
默认的通用仓储的默认实现是`EfCoreRepository`类,你可以创建自己的实现,并将其做为默认实现
首先,像这样定义仓储类:
```csharp
public class MyRepositoryBase<TEntity>
: EfCoreRepository<BookStoreDbContext, TEntity>
where TEntity : class, IEntity
{
public MyRepositoryBase(IDbContextProvider<BookStoreDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
public class MyRepositoryBase<TEntity, TKey>
: EfCoreRepository<BookStoreDbContext, TEntity, TKey>
where TEntity : class, IEntity<TKey>
{
public MyRepositoryBase(IDbContextProvider<BookStoreDbContext> dbContextProvider)
: base(dbContextProvider)
{
}
}
```
第一个用于具有[复合主键的实体](Entities.md),第二个用于具有单个主键的实体
建议从`EfCoreRepository`类继承并在需要时重写方法. 否则,你需要手动实现所有标准仓储方法.
现在,你可以使用SetDefaultRepositoryClasses Options
```csharp
context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
{
options.SetDefaultRepositoryClasses(
typeof(MyRepositoryBase<,>),
typeof(MyRepositoryBase<>)
);
//...
});
```
#### 为默认仓储设置Base DbContext类或接口
TODO ...
如果你的DbContext继承了另外一个DbContext或实现了一个接口,你可以使用这个基类或接口作为默认仓储的DbContext. 例:
````csharp
public interface IBookStoreDbContext : IEfCoreDbContext
{
DbSet<Book> Books { get; }
}
````
`IBookStoreDbContext`接口是由`BookStoreDbContext`实现的. 然后你可以使用`AddDefaultRepositories`的泛型重载.
````csharp
context.Services.AddAbpDbContext<BookStoreDbContext>(options =>
{
options.AddDefaultRepositories<IBookStoreDbContext>();
//...
});
````
现在,您的自定义仓储也可以使用`IBookStoreDbContext`接口:
````csharp
public class BookRepository : EfCoreRepository<IBookStoreDbContext, Book, Guid>, IBookRepository
{
//...
}
````
使用DbContext接口的一个优点是它可以被其他实现替换.
#### 替换其他仓储
TODO ...
正确定义并使用DbContext接口后,任何其他实现都可以使用以下ReplaceDbContext options 替换它:
````csharp
context.Services.AddAbpDbContext<OtherDbContext>(options =>
{
//...
options.ReplaceDbContext<IBookStoreDbContext>();
});
````
在这个例子中,`OtherDbContext`实现了`IBookStoreDbContext`. 此功能允许你在开发时使用多个DbContext(每个模块一个),但在运行时可以使用单个DbContext(实现所有DbContext的所有接口).

2
docs/zh-Hans/Exception-Handling.md

@ -81,7 +81,7 @@ ABP提供了用于处理Web应用程序异常的标准模型.
}
````
`AbpValidationException`已经实现了`IHasValidationErrors`接口,当请求输入无效时,框架会自动抛出此错误. 因此,除非有自定义的验证逻辑,否则不需要处理验证错误.
`AbpValidationException`已经实现了`IHasValidationErrors`接口,当请求输入无效时,框架会自动抛出此错误. 因此,除非有自定义的验证逻辑,否则不需要处理验证错误.
#### 日志

2
docs/zh-Hans/Getting-Started-AspNetCore-MVC-Template.md

@ -60,7 +60,7 @@ The solution is configured to use **Entity Framework Core** with **MS SQL Server
### 运行应用程序
现在可以运行应用程序,它将会打开**home**页面:
现在可以运行应用程序,它将会打开**home**页面:
![bookstore-homepage](images/bookstore-homepage.png)

4
docs/zh-Hans/Getting-Started-Console-Application.md

@ -1,6 +1,6 @@
## 使用Console Application
本教程将介绍如何从头开始以最小的依赖关系启动ABP. 通常希望以 **[启动模板](https://abp.io/Templates)** 开头.
本教程将介绍如何从头开始以最小的依赖关系启动ABP. 通常希望以 **[启动模板](https://abp.io/Templates)** 开头.
### 创建一个新项目
@ -10,7 +10,7 @@
### 安装 Volo.Abp 包
Volo.Abp.Core是创建基于ABP的应用程序的核心nuget包. 所以,将它安装到的项目中:
Volo.Abp.Core是创建基于ABP的应用程序的核心nuget包. 所以,将它安装到的项目中:
````
Install-Package Volo.Abp.Core

4
docs/zh-Hans/Localization.md

@ -12,7 +12,7 @@ Volo.Abp.Localization是本地化系统的核心包. 使用程序包管理器控
Install-Package Volo.Abp.Localization
```
然后,可以将 **AbpLocalizationModule** 依赖项添加到您的模块:
然后,可以将 **AbpLocalizationModule** 依赖项添加到模块:
```c#
using Volo.Abp.Modularity;
@ -126,7 +126,7 @@ services.Configure<AbpLocalizationOptions>(options =>
##### 扩展现有资源
继承资源可以创建新的资源, 无需修改现有的资源. 但是在某些情况下, 可能不想创建新资源,而是直接扩展现有资源. 例如:
继承资源可以创建新的资源, 无需修改现有的资源. 但是在某些情况下, 可能不想创建新资源,而是直接扩展现有资源. 例如:
````C#
services.Configure<AbpLocalizationOptions>(options =>

2
docs/zh-Hans/Repositories.md

@ -76,7 +76,7 @@ ABP提供了 `IBasicRepository<TEntity, TPrimaryKey>` 和 `IBasicRepository<TEnt
#### 自定义仓储示例
ABP不会强制实现任何接口或从存储库的任何基类继承. 它可以只是一个简单的POCO类. 但是建议继承现有的仓储接口和类, 获得开箱即用的标准方法使你的工作更轻松.
ABP不会强制实现任何接口或从存储库的任何基类继承. 它可以只是一个简单的POCO类. 但是建议继承现有的仓储接口和类, 获得开箱即用的标准方法使你的工作更轻松.
##### 自定义仓储接口

2
docs/zh-Hans/Virtual-File-System.md

@ -107,7 +107,7 @@ public class MyService
通过引用程序集(或添加nuget包)将文件嵌入模块程序集并从另一个项目中使用它对于创建可重用模块非常有价值. 但是, 这使得开发模块本身变得有点困难.
假设正在开发一个包含嵌入式JavaScript文件的模块. 当你更改文件时, 你必须重新编译项目, 重新启动应用程序并刷新浏览器页面以进行更改. 显然, 这是非常耗时和乏味的.
假设正在开发一个包含嵌入式JavaScript文件的模块. 当你更改文件时, 你必须重新编译项目, 重新启动应用程序并刷新浏览器页面以进行更改. 显然, 这是非常耗时和乏味的.
我们需要的是应用程序在开发时直接使用物理文件的能力, 让浏览器刷新时同步JavaScript文件的任何更改. `ReplaceEmbeddedByPyhsical` 方法使其成为可能.

Loading…
Cancel
Save