Merge pull request #1763 from brittos/dev

Create docs-nav.json
pull/1767/head
maliming 6 years ago committed by GitHub
commit 2a83b9ee8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,3 @@
## Dynamic Proxying / Interceptors
Façam

@ -0,0 +1,3 @@
# Audit Logging
Façam

@ -0,0 +1,3 @@
## Authorization
Façam

@ -0,0 +1,3 @@
## AutoMapper Integration
Façam

@ -0,0 +1,3 @@
# Hangfire Background Job Manager
Façam

@ -0,0 +1,3 @@
# RabbitMQ Background Job Manager
TODO

@ -0,0 +1,149 @@
# ABP CLI
O ABP CLI (Command Line Interface) é uma ferramenta de linha de comando para executar algumas operações comuns para soluções baseadas em ABP.
## Instalação
O ABP CLI é uma [ferramenta global dotnet](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools) . Instale-o usando uma janela de linha de comando:
````bash
dotnet tool install -g Volo.Abp.Cli
````
Para atualizar uma instalação existente:
````bash
dotnet tool update -g Volo.Abp.Cli
````
## Comandos
### Novo
Gera uma nova solução baseada nos [modelos de inicialização](Startup-Templates/Index.md) do ABP .
Uso básico:
````bash
abp new <solution-name> [options]
````
Examplo:
````bash
abp new Acme.BookStore
````
* `Acme.BookStore` é o nome da solução aqui.
* A convenção comum é nomear uma solução como *YourCompany.YourProject* . No entanto, você pode usar nomes diferentes, como *YourProject* (namespacing de nível único) ou *YourCompany.YourProduct.YourModule* (namespacing de três níveis).
#### Opções
* `--template`ou `-t`: especifica o nome do modelo. O nome do modelo padrão é `app`, que gera um aplicativo da web. Modelos disponíveis:
* `app`(padrão): [modelo de aplicativo](https://docs.abp.io/en/abp/latest/Startup-Templates/Application) . Opções adicionais:
* `--ui`ou `-u`: Especifica a UI framework. Framework padrão é `mvc`. Framework disponíveis:
* `mvc`: ASP.NET Core MVC. Existem algumas opções adicionais para este modelo:
* `--tiered`: Cria uma solução em camadas em que as camadas da Web e da API HTTP são fisicamente separadas. Se não especificado, ele cria uma solução em camadas que é menos complexa e adequada para a maioria dos cenários.
* `angular`: Angular. Existem algumas opções adicionais para este modelo:
* `--separate-identity-server`: Separa o aplicativo do servidor de identidade do aplicativo host da API. Se não especificado, você terá um único ponto de extremidade no lado do servidor.
* `--database-provider` Ou `-d`: especifica o provedor de banco de dados. O provedor padrão é `ef`. Fornecedores disponíveis:
* `ef`: Entity Framework Core.
* `mongodb`: MongoDB.
* `module`: [Exemplo de Módulo](Startup-Templates/Module.md). Opções adicionais:
* `--no-ui`: Especifica para não incluir a UI. Isso possibilita a criação de módulos somente de serviço (também conhecidos como microsserviços - sem interface do usuário).
* `--output-folder` ou `-o`: especifica a pasta de saída. O valor padrão é o diretório atual.
* `--version` ou `-v`: Especifica a ABP & versão de exemplo . Pode ser uma [release tag](https://github.com/abpframework/abp/releases) ou um [branch name](https://github.com/abpframework/abp/branches). Usa a versão mais recente, se não especificado. Na maioria das vezes, você desejará usar a versão mais recente.
### add-package
Adiciona um pacote ABP a um projeto por,
- Adicionando pacote de nuget relacionado como uma dependência ao projeto.
- Adicionando `[DependsOn(...)]`atributo à classe de módulo no projeto (consulte o [documento de desenvolvimento](https://docs.abp.io/en/abp/latest/Module-Development-Basics) do [módulo](https://docs.abp.io/en/abp/latest/Module-Development-Basics) ).
> Observe que o módulo adicionado pode exigir uma configuração adicional, geralmente indicada na documentação do pacote relacionado.
Uso básico:
```bash
abp add-package <package-name> [options]
```
Bater
cópia de
Exemplo:
```
abp add-package Volo.Abp.MongoDB
```
- Este exemplo adiciona o pacote Volo.Abp.MongoDB ao projeto.
#### Opções
- `--project`ou `-p`: especifica o caminho do arquivo do projeto (.csproj). Se não especificado, a CLI tenta encontrar um arquivo .csproj no diretório atual.
### add-module
Adiciona um [módulo de aplicativo com vários pacotes](Modules/Index.md) a uma solução, localizando todos os pacotes do módulo, localizando projetos relacionados na solução e adicionando cada pacote ao projeto correspondente na solução.
> Um módulo de negócios geralmente consiste em vários pacotes (devido a camadas, diferentes opções de provedor de banco de dados ou outros motivos). O uso do `add-module`comando simplifica drasticamente a adição de um módulo a uma solução. No entanto, cada módulo pode exigir algumas configurações adicionais, geralmente indicadas na documentação do módulo relacionado.
Uso básico:
```bash
abp add-module <module-name> [options]
```
Exemplo:
```bash
abp add-module Volo.Blogging
```
- Este exemplo adiciona o módulo Volo.Blogging à solução.
#### Opções
- `--solution`ou `-s`: especifica o caminho do arquivo da solução (.sln). Se não especificado, a CLI tenta encontrar um arquivo .sln no diretório atual.
- `--skip-db-migrations`: Para o provedor de banco de dados EF Core, ele adiciona automaticamente um novo código à primeira migração ( `Add-Migration`) e atualiza o banco de dados ( `Update-Database`), se necessário. Especifique esta opção para pular esta operação.
- `-sp`ou `--startup-project`: caminho relativo para a pasta do projeto de inicialização. O valor padrão é a pasta atual.
### atualizar
A atualização de todos os pacotes relacionados ao ABP pode ser entediante, pois existem muitos pacotes da estrutura e dos módulos. Este comando atualiza automaticamente todos os pacotes NuGet e NPM relacionados ao ABP em uma solução ou projeto para as versões mais recentes.
Uso:
```bash
abp update [options]
```
- Se você executar em um diretório com um arquivo .sln, ele atualizará todos os pacotes relacionados ao ABP de todos os projetos da solução para as versões mais recentes.
- Se você executar em um diretório com um arquivo .csproj, ele atualizará todos os pacotes relacionados ao ABP do projeto para as versões mais recentes.
#### Opções
- `--include-previews`ou `-p`: inclui pacotes de visualização, beta e rc enquanto verifica as versões mais recentes.
### Socorro
Grava informações básicas de uso da CLI.
Uso:
```bash
abp help [command-name]
```
Exemplos:
```bash
abp help # Shows a general help.
abp help new # Shows help about the "new" command.
```

@ -0,0 +1,3 @@
# Caching
Façam

@ -0,0 +1,3 @@
# Correlation ID
Façam

@ -0,0 +1,3 @@
# Data Filtering
Façam

@ -0,0 +1,3 @@
# Data Seeding
Façam

@ -0,0 +1,3 @@
## Data Transfer Objects
Façam

@ -0,0 +1,3 @@
# Distributed Event Bus
Façam

@ -0,0 +1,3 @@
# ABP Documentation
Façam!

@ -0,0 +1,3 @@
## Dynamic Proxying / Interceptors
Façam

@ -0,0 +1,3 @@
# Emailing
Façam!

@ -0,0 +1,3 @@
# Event Bus
Façam

@ -0,0 +1,3 @@
# Extension Methods & Helpers
Façam

@ -0,0 +1,3 @@
## Guid Generation
Façam

@ -1,12 +1,12 @@
# Documentação ABP
O ABP é uma **estrutura de aplicativos de código aberto** focada no desenvolvimento de aplicativos da Web baseado no ASP.NET Core, mas também suporta o desenvolvimento de outros tipos de aplicativos.
O ABP é um **framework de aplicativos de código aberto** focada no desenvolvimento de aplicativos da Web baseado no ASP.NET Core, mas também suporta o desenvolvimento de outros tipos de aplicativos.
Explore o menu de navegação esquerdo para mergulhar fundo na documentação.
## Status do projeto
ABP é a **próxima geração** da estrutura de código aberto [ASP.NET Boilerplate](https://aspnetboilerplate.com/) . Atualmente, está em fase de visualização e não está pronto para uso na produção. A documentação ainda está em andamento e está longe de estar completa.
ABP é a **próxima geração** de framework de código aberto [ASP.NET Boilerplate](https://aspnetboilerplate.com/). Atualmente, está em fase de pré-visualização e não está pronto para uso na produção. A documentação ainda está em andamento e está longe de estar completa.
Para aplicativos de curto prazo e em nível de produção, é sugerido o uso da estrutura do [ASP.NET Boilerplate](https://aspnetboilerplate.com/) , que possui um rico conjunto de recursos, maduro, mantido ativamente e atualizado.

@ -0,0 +1,3 @@
# Integration Tests
Façam!

@ -0,0 +1,3 @@
# Local Event Bus
Façam

@ -0,0 +1,195 @@
# Localização
O sistema de localização da ABP é perfeitamente integrado ao `Microsoft.Extensions.Localization`pacote e compatível com a [documentação de localização da Microsoft](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) . Ele adiciona alguns recursos e aprimoramentos úteis para facilitar o uso em cenários de aplicativos da vida real.
## Pacote Volo.Abp.Localization
> Este pacote já está instalado por padrão com o modelo de inicialização. Portanto, na maioria das vezes, você não precisa instalá-lo manualmente.
Volo.Abp.Localization é o pacote principal do sistema de localização. Instale-o no seu projeto usando o console do gerenciador de pacotes (PMC):
```
Install-Package Volo.Abp.Localization
```
Em seguida, você pode adicionar a dependência **AbpLocalizationModule** ao seu módulo:
```csharp
using Volo.Abp.Modularity;
using Volo.Abp.Localization;
namespace MyCompany.MyProject
{
[DependsOn(typeof(AbpLocalizationModule))]
public class MyModule : AbpModule
{
//...
}
}
```
## Criando um recurso de localização
Um recurso de localização é usado para agrupar cadeias de localização relacionadas e separá-las de outras cadeias de localização do aplicativo. Um [módulo](Module-Development-Basics.md) geralmente define seu próprio recurso de localização. O recurso de localização é apenas uma classe simples. Exemplo:
```csharp
public class TestResource
{
}
```
Em seguida, deve ser adicionado usando `AbpLocalizationOptions`como mostrado abaixo:
```csharp
[DependsOn(typeof(AbpLocalizationModule))]
public class MyModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<VirtualFileSystemOptions>(options =>
{
options.FileSets.AddEmbedded<MyModule>();
});
Configure<AbpLocalizationOptions>(options =>
{
//Define a new localization resource (TestResource)
options.Resources
.Add<TestResource>("en")
.AddVirtualJson("/Localization/Resources/Test");
});
}
}
```
Neste exemplo;
- Adicionado um novo recurso de localização com "en" (inglês) como a cultura padrão.
- Arquivos JSON usados para armazenar as sequências de localização.
- Os arquivos JSON são incorporados ao assembly usando `VirtualFileSystemOptions`(consulte [sistema de arquivos virtual](Virtual-File-System.md) ).
Os arquivos JSON estão localizados na pasta do projeto "/ Localização / Recursos / Teste", como mostrado abaixo:
![localization-resource-json-files](images/localization-resource-json-files.png)
Um conteúdo do arquivo de localização JSON é mostrado abaixo:
```json
{
"culture": "en",
"texts": {
"HelloWorld": "Hello World!"
}
}
```
- Todo arquivo de localização deve definir o `culture`código para o arquivo (como "en" ou "en-US").
- `texts` A seção contém apenas a coleção de valores-chave das sequências de localização (as chaves também podem ter espaços).
### Nome Curto do Recurso de Localização
Os recursos de localização também estão disponíveis no lado do cliente (JavaScript). Portanto, definir um nome abreviado para o recurso de localização facilita o uso de textos de localização. Exemplo:
```csharp
[LocalizationResourceName("Test")]
public class TestResource
{
}
```
Consulte a seção Obtendo teste localizado / lado do cliente abaixo.
### Herdar de outros recursos
Um recurso pode herdar de outros recursos, o que possibilita reutilizar cadeias de localização existentes sem fazer referência ao recurso existente. Exemplo:
```csharp
[InheritResource(typeof(AbpValidationResource))]
public class TestResource
{
}
```
Herança alternativa configurando o `AbpLocalizationOptions`:
```csharp
services.Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Add<TestResource>("en") //Define the resource by "en" default culture
.AddVirtualJson("/Localization/Resources/Test") //Add strings from virtual json files
.AddBaseTypes(typeof(AbpValidationResource)); //Inherit from an existing resource
});
```
- Um recurso pode herdar de vários recursos.
- Se o novo recurso definir a mesma sequência localizada, ele substituirá a sequência.
### Estendendo o Recurso Existente
Herdar de um recurso cria um novo recurso sem modificar o existente. Em alguns casos, convém não criar um novo recurso, mas estender diretamente um recurso existente. Exemplo:
```csharp
services.Configure<AbpLocalizationOptions>(options =>
{
options.Resources
.Get<TestResource>()
.AddVirtualJson("/Localization/Resources/Test/Extensions");
});
```
- Se um arquivo de extensão define a mesma sequência localizada, ele substitui a sequência.
## Obtendo textos localizados
### Lado do servidor
Obter o texto localizado no lado do servidor é bastante padrão.
#### Uso mais simples de uma classe
```csharp
public class MyService
{
private readonly IStringLocalizer<TestResource> _localizer;
public MyService(IStringLocalizer<TestResource> localizer)
{
_localizer = localizer;
}
public void Foo()
{
var str = _localizer["HelloWorld"];
}
}
```
#### Uso mais simples em uma vista / página do Razor
```csharp
@inject IHtmlLocalizer<TestResource> Localizer
<h1>@Localizer["HelloWorld"]</h1>
```
Consulte a [documentação de localização da Microsoft](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/localization) para obter detalhes sobre o uso da localização no lado do servidor.
### Lado do Cliente
A ABP fornece serviços JavaScript para usar os mesmos textos localizados no lado do cliente.
Obtenha um recurso de localização:
```js
var testResource = abp.localization.getResource('Test');
```
Localize uma sequência:
```js
var str = testResource('HelloWorld');
```

@ -0,0 +1,27 @@
# Compilações Noturnas
Todos os pacotes de estrutura e módulo são implantados no MyGet todas as noites durante a semana. Portanto, você pode usar ou testar o código mais recente sem aguardar o próximo lançamento.
## Configurar o Visual Studio
> Requer Visual Studio 2017 ou superior
1. Vá para `Tools > Options > NuGet Package Manager > Package Source`.
2. Clique no `+` ícone verde .
3. Defina `ABP Nightly`como *Nome* e `https://www.myget.org/F/abp-nightly/api/v3/index.json`como a *Fonte,* como mostrado abaixo: ![noite-compilação-adicionar-pepita-fonte](images/night-build-add-nuget-source.png)
4. Clique no `Update` botão
5. Clique no `OK` botão para salvar as alterações.
## Instalar pacote
Agora, você pode instalar pacotes noturnos / de visualização no seu projeto a partir do Nuget Browser ou do Package Manager Console.
![night-build-add-nuget-package](images/night-build-add-nuget-package.png)
1. No Nuget Browser, selecione "Incluir pré-lançamentos".
2. Altere a fonte do pacote para "Todos".
3. Pesquise um pacote. Você verá as pré *-liberações* do pacote formatadas como `(VERSION)-preview(DATE)`(como *v0.16.0-preview20190401* neste exemplo).
4. Você pode clicar no `Install`botão para adicionar um pacote ao seu projeto.

@ -0,0 +1,3 @@
## Object To Object Mapping
Façam

@ -0,0 +1,3 @@
# Emailing
Façam!

@ -0,0 +1,3 @@
# Settings
Façam!

@ -0,0 +1,3 @@
# Specifications
Façam!

@ -0,0 +1,275 @@
# Modelo de inicialização do aplicativo
## Introdução
Este modelo fornece uma estrutura de aplicativo em camadas com base nas práticas DDD ([Domain Driven Design](../Domain-Driven-Design.md)). Este documento explica a estrutura da solução e os projetos em detalhes. Se você deseja iniciar rapidamente, siga os guias abaixo:
- Consulte [Introdução ao modelo do ASP.NET Core MVC](../Getting-Started-AspNetCore-MVC-Template.md) para criar uma nova solução e executá-la para este modelo (usa o MVC como a estrutura da interface do usuário e o Entity Framework Core como o provedor de banco de dados).
- Consulte o [Tutorial de desenvolvimento de aplicativos do ASP.NET Core MVC](../Tutorials/AspNetCore-Mvc/Part-I.md) para aprender como desenvolver aplicativos usando este modelo (usa o MVC como a estrutura da interface do usuário e o Entity Framework Core como o provedor de banco de dados).
- Consulte o [Tutorial de desenvolvimento de aplicativos Angular](../Tutorials/Angular/Part-I.md) para aprender como desenvolver aplicativos usando este modelo (usa Angular como a estrutura da interface do usuário e MongoDB como o provedor de banco de dados).
## Como começar?
Você pode usar a [ABP CLI](../CLI.md) para criar um novo projeto usando este modelo de inicialização. Como alternativa, você pode criar e fazer o download diretamente na página [Introdução](https://abp.io/get-started) . A abordagem CLI é usada aqui.
Primeiro, instale a ABP CLI se você não tiver instalado antes:
```bash
dotnet tool install -g Volo.Abp.Cli
```
Em seguida, use o `abp new`comando em uma pasta vazia para criar uma nova solução:
```bash
abp new Acme.BookStore -t app
```
- `Acme.BookStore`é o nome da solução, como *YourCompany.YourProduct* . Você pode usar nomes de nível único, dois ou três níveis.
- Este exemplo especificou o nome do modelo ( `-t`ou `--template`opção). No entanto, `app`já é o modelo padrão se você não o especificar.
### Especifique a estrutura da interface do usuário
Este modelo fornece várias estruturas de interface do usuário:
- `mvc`: Interface do usuário do ASP.NET Core MVC com Razor Pages (padrão)
- `angular`: UI angular
Use `-u`ou `--ui`opção para especificar a estrutura da interface do usuário:
```bash
abp new Acme.BookStore -u angular
```
### Especifique o provedor de banco de dados
Este modelo suporta os seguintes provedores de banco de dados:
- `ef`: Entity Framework Core (padrão)
- `mongodb`: MongoDB
Use a opção `-d`(ou `--database-provider`) para especificar o provedor de banco de dados:
```bash
abp new Acme.BookStore -d mongodb
```
## Estrutura da solução
Com base nas opções especificadas, você obterá uma estrutura de solução ligeiramente diferente.
### Estrutura padrão
Se você não especificar nenhuma opção adicional, terá uma solução como a mostrada abaixo:
![livraria-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-v3.png)
Os projetos são organizados em `src`e `test`pastas. `src`A pasta contém o aplicativo real que está em camadas com base nos princípios [DDD](https://docs.abp.io/en/abp/latest/Domain-Driven-Design) , como mencionado anteriormente.
O diagrama abaixo mostra as camadas e dependências do projeto do aplicativo:
![dependências de projeto em camadas](../images/layered-project-dependencies.png)
Cada seção abaixo explicará o projeto relacionado e suas dependências.
#### Projeto .Domain.Shared
Este projeto contém constantes, enumerações e outros objetos. Na verdade, eles fazem parte da camada de domínio, mas precisam ser usados por todas as camadas / projetos da solução.
Um `BookType`enum e uma `BookConsts`classe (que podem ter alguns campos constantes para a `Book`entidade, como `MaxNameLength`) são bons candidatos para este projeto.
- Este projeto não depende de outros projetos na solução. Todos os outros projetos dependem disso direta ou indiretamente.
#### .Domain Project
Essa é a camada de domínio da solução. Ele contém principalmente [entidades, raízes agregadas](../Entities.md) , [serviços de domínio](../Domain-Services.md) , [tipos de valor](../Value-Types.md) , [interfaces de repositório](../Repositories) e outros objetos de domínio.
Uma `Book`entidade, um `BookManager`serviço de domínio e uma `IBookRepository`interface são bons candidatos para este projeto.
- Depende do `.Domain.Shared`porque usa constantes, enumerações e outros objetos definidos nesse projeto.
#### .Application.Contracts Project
Este projeto contém principalmente **interfaces de** [serviço de aplicativo](../Application-Services.md) e DTO ( [Data Transfer Objects](../Data-Transfer-Objects.md) ) da camada de aplicativo. Existe para separar a interface e a implementação da camada de aplicação. Dessa forma, o projeto de interface pode ser compartilhado com os clientes como um pacote de contrato.
Uma `IBookAppService`interface e uma `BookCreationDto`classe são boas candidatas para este projeto.
- Depende do `.Domain.Shared`porque ele pode usar constantes, enumerações e outros objetos compartilhados deste projeto nas interfaces de serviço de aplicativo e DTOs.
#### Projeto de Aplicação
Este projeto contém as **implementações** de [serviço de aplicativo](../Application-Services.md) das interfaces definidas no projeto.`.Application.Contracts`
Uma `BookAppService`turma é uma boa candidata para este projeto.
- Depende do `.Application.Contracts`projeto para poder implementar as interfaces e usar os DTOs.
- Depende do `.Domain`projeto para poder usar objetos de domínio (entidades, interfaces de repositório ... etc.) para executar a lógica do aplicativo.
#### Projeto .EntityFrameworkCore
Este é o projeto de integração para o EF Core. Ele define `DbContext`e implementa as interfaces de repositório definidas no `.Domain`projeto.
- Depende do `.Domain`projeto para poder fazer referência a entidades e interfaces de repositório.
> Este projeto está disponível apenas se você estiver usando o EF Core como provedor de banco de dados. Se você selecionar outro provedor de banco de dados, seu nome será diferente.
#### Projeto .EntityFrameworkCore.DbMigrations
Contém migrações de banco de dados EF Core para a solução. Ele foi separado `DbContext`para dedicado a gerenciar migrações.
ABP é uma estrutura modular e com um design ideal, cada módulo tem sua própria `DbContext`classe. É aqui que a migração `DbContext`entra em ação e unifica todas as `DbContext`configurações em um único modelo para manter um único esquema de banco de dados. Para cenários mais avançados, você pode ter vários bancos de dados (cada um contém uma única ou algumas tabelas de módulos) e várias migrações `DbContext`(cada uma mantém um esquema de banco de dados diferente).
Observe que a migração `DbContext`é usada apenas para migrações de banco de dados e *não em tempo de execução* .
- Depende do `.EntityFrameworkCore`projeto, pois reutiliza a configuração definida para `DbContext`o aplicativo.
> Este projeto está disponível apenas se você estiver usando o EF Core como provedor de banco de dados.
#### Projeto .DbMigrator
Este é um aplicativo de console que simplifica a execução de migrações de banco de dados em ambientes de desenvolvimento e produção. Quando você executa este aplicativo, ele;
- Cria o banco de dados, se necessário.
- Aplica as migrações de banco de dados pendentes.
- Semeia os dados iniciais, se necessário.
> Este projeto possui seu próprio `appsettings.json`arquivo. Portanto, se você deseja alterar a cadeia de conexão do banco de dados, lembre-se de alterar também esse arquivo.
Especialmente, semear dados iniciais é importante neste momento. A ABP possui uma infraestrutura modular de semente de dados. Consulte [a documentação](../Data-Seeding.md) para obter mais informações sobre a propagação de dados.
Embora a criação de banco de dados e a aplicação de migrações pareça necessária apenas para bancos de dados relacionais, esse projeto ocorre mesmo que você escolha um provedor de banco de dados NoSQL (como o MongoDB). Nesse caso, ele ainda semeia os dados iniciais necessários para a aplicação.
- Depende do `.EntityFrameworkCore.DbMigrations`projeto (para EF Core), pois ele precisa acessar as migrações.
- Depende do `.Application.Contracts`projeto para poder acessar as definições de permissão, porque o semeador de dados inicial concede todas as permissões para a função de administrador por padrão.
#### Projeto .HttpApi
Este projeto é usado para definir seus controladores de API.
Na maioria das vezes, você não precisa definir manualmente os controladores de API, pois o recurso de [controladores de API automática](../AspNetCore/Auto-API-Controllers.md) da ABP os cria automaticamente, com base na sua camada de aplicação. No entanto, no caso de você precisar escrever controladores de API, este é o melhor lugar para fazê-lo.
- Depende do `.Application.Contracts`projeto para poder injetar as interfaces de serviço do aplicativo.
#### Projeto .HttpApi.Client
Este é um projeto que define os proxies do cliente C # para usar as APIs HTTP da solução. Você pode compartilhar essa biblioteca com clientes de terceiros, para que eles consumam facilmente suas APIs HTTP em seus aplicativos Dotnet (para outros tipos de aplicativos, eles ainda podem usar suas APIs, manualmente ou usando uma ferramenta em sua própria plataforma)
Na maioria das vezes, você não precisa criar proxies de clientes C # manualmente, graças ao recurso [Dynamic C # API Clients](../AspNetCore/Dynamic-CSharp-API-Clients.md) da ABP .
`.HttpApi.Client.ConsoleTestApp` project é um aplicativo de console criado para demonstrar o uso dos proxies do cliente.
- Depende do `.Application.Contracts`projeto para poder compartilhar as mesmas interfaces de serviço de aplicativo e DTOs com o serviço remoto.
> Você pode excluir este projeto e dependências se não precisar criar proxies de cliente C # para suas APIs.
#### Projeto .Web
Este projeto contém a interface do usuário (UI) do aplicativo se você estiver usando a interface do usuário do ASP.NET Core MVC. Ele contém páginas Razor, arquivos JavaScript, arquivos CSS, imagens e assim por diante ...
Este projeto contém o `appsettings.json`arquivo principal que contém a cadeia de conexão e outras configurações do aplicativo.
- Depende da `.HttpApi`camada de interface do usuário que precisa usar APIs e interfaces de serviço de aplicativo da solução.
> Se você verificar o código fonte do `.Web.csproj`arquivo, verá as referências aos `.Application`e aos `.EntityFrameworkCore.DbMigrations`projetos.
>
> Na verdade, essas referências não são necessárias durante a codificação da camada da interface do usuário, porque a camada da interface do usuário normalmente não depende da implementação do EF Core ou da camada do Aplicativo. Esses modelos de inicialização estão prontos para a implantação em camadas, em que a camada da API está hospedada em um servidor separado da camada da interface do usuário.
>
> No entanto, se você não escolher a opção `--tiered`, essas referências estarão no projeto .Web para poder hospedar as camadas da Web, API e aplicativos em um único ponto de extremidade do aplicativo.
>
> Isso permite que você use entidades e repositórios de domínio em sua camada de apresentação. No entanto, isso é considerado uma má prática de acordo com o DDD.
#### Projetos de teste
A solução possui vários projetos de teste, um para cada camada:
- `.Domain.Tests` é usado para testar a camada de domínio.
- `.Application.Tests` é usado para testar a camada de aplicativo.
- `.EntityFrameworkCore.Tests` é usado para testar a configuração do EF Core e os repositórios personalizados.
- `.Web.Tests` é usado para testar a interface do usuário (se você estiver usando a interface do ASP.NET Core MVC).
- `.TestBase` é um projeto básico (compartilhado) para todos os testes.
Além disso, `.HttpApi.Client.ConsoleTestApp`é um aplicativo de console (não um projeto de teste automatizado) que demonstra o uso de APIs HTTP de um aplicativo .NET.
Projetos de teste são preparados para testes de integração;
- É totalmente integrado à estrutura ABP e a todos os serviços em sua aplicação.
- Ele usa o banco de dados SQLite na memória para o EF Core. Para o MongoDB, ele usa a biblioteca [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) .
- A autorização está desabilitada, portanto, qualquer serviço de aplicativo pode ser facilmente usado em testes.
Você ainda pode criar testes de unidade para suas classes, que serão mais difíceis de escrever (porque você precisará preparar objetos simulados / falsos), mas mais rápidos de executar (porque apenas testa uma única classe e ignora todo o processo de inicialização).
#### Como correr?
Defina `.Web`como o projeto de inicialização e execute o aplicativo. O nome de usuário padrão é `admin`e a senha é `1q2w3E*`.
Consulte [Introdução ao modelo ASP.NET Core MVC](../Getting-Started-AspNetCore-MVC-Template.md) para obter mais informações.
### Estrutura em camadas
Se você selecionou a interface do usuário do ASP.NET Core e especificou a `--tiered`opção, a solução criada será uma solução em camadas. O objetivo da estrutura em camadas é poder **implantar aplicativos da Web e API HTTP em diferentes servidores** :
![livraria-visual-studio-solution-v3](../images/tiered-solution-servers.png)
- O navegador executa sua interface do usuário executando HTML, CSS e JavaScript.
- Os servidores da Web hospedam arquivos de interface do usuário estáticos (CSS, JavaScript, imagem ... etc.) e componentes dinâmicos (por exemplo, páginas Razor). Ele executa solicitações HTTP para o servidor da API para executar a lógica de negócios do aplicativo.
- O API Server hospeda as APIs HTTP que, em seguida, usam as camadas de aplicativo e domínio do aplicativo para executar a lógica de negócios.
- Finalmente, o servidor de banco de dados hospeda seu banco de dados.
Portanto, a solução resultante permite uma implantação em quatro camadas, comparando com a implantação em três camadas da estrutura padrão explicada anteriormente.
> A menos que você realmente precise de uma implantação em quatro camadas, é recomendável seguir a estrutura padrão que é mais simples de desenvolver, implantar e manter.
A estrutura da solução é mostrada abaixo:
![livraria-visual-studio-solution-v3](../images/bookstore-visual-studio-solution-tiered.png)
Diferente da estrutura padrão, dois novos projetos entram em jogo: `.IdentityServer`& `.HttpApi.Host`.
#### Projeto .IdentityServer
Este projeto é usado como um servidor de autenticação para outros projetos. `.Web`O projeto usa a autenticação do OpenId Connect para obter tokens de identidade e acesso para o usuário atual do IdentityServer. Em seguida, usa o token de acesso para chamar o servidor da API HTTP. O servidor HTTP API usa autenticação de token de portador para obter declarações do token de acesso para autorizar o usuário atual.
![aplicativos de solução em camadas](../images/tiered-solution-applications.png)
O ABP usa a estrutura [IdentityServer4 de](https://identityserver.io/) código aberto para a autenticação entre aplicativos. Consulte a [documentação do IdentityServer4](http://docs.identityserver.io/) para obter detalhes sobre o protocolo IdentityServer4 e OpenID Connect.
Ele possui seu próprio `appsettings.json`que contém conexão com o banco de dados e outras configurações.
#### Projeto .HttpApi.Host
Este projeto é um aplicativo que hospeda a API da solução. Ele possui seu próprio `appsettings.json`que contém conexão com o banco de dados e outras configurações.
#### Projeto .Web
Assim como a estrutura padrão, este projeto contém a interface do usuário (UI) do aplicativo. Ele contém páginas de barbear, arquivos JavaScript, arquivos de estilo, imagens e assim por diante ...
Este projeto contém um `appsettings.json`arquivo, mas desta vez não possui uma cadeia de conexão porque nunca se conecta ao banco de dados. Em vez disso, ele contém principalmente o terminal do servidor de API remoto e o servidor de autenticação.
#### Pré requisitos
- [Redis](https://redis.io/) : os aplicativos usam Redis como cache distribuído. Então, você precisa ter o Redis instalado e funcionando.
#### Como correr?
Você deve executar o aplicativo com a ordem especificada:
- Primeiro, execute o `.IdentityServer`aplicativo, pois outros aplicativos dependem dele.
- Em seguida, execute o `.HttpApi.Host`que é usado pelo `.Web`aplicativo.
- Por fim, você pode executar o `.Web`projeto e efetuar login no aplicativo (usando `admin`como nome de usuário e `1q2w3E*`senha).
### UI angular
Se você escolher Angular como a estrutura da interface do usuário (usando a `-u angular`opção), a solução será separada em duas pastas:
- `angular` A pasta contém a solução Angular UI, do lado do cliente.
- `aspnet-core` A pasta contém a solução ASP.NET Core, do lado do servidor.
O lado do servidor é muito semelhante à solução descrita acima. `.HttpApi.Host`projeto serve a API, para que o aplicativo Angular possa consumi-lo.
Os arquivos na `angular/src/environments`pasta têm a configuração essencial do aplicativo.
## Qual é o próximo?
- Consulte [Introdução ao modelo ASP.NET Core MVC](../Getting-Started-AspNetCore-MVC-Template.md) para criar uma nova solução e executá-la para este modelo.
- Consulte o [Tutorial](../Tutorials/AspNetCore-Mvc/Part-I.md) do [ASP.NET Core MVC](../Tutorials/AspNetCore-Mvc/Part-I.md) para aprender como desenvolver aplicativos usando este modelo.

@ -0,0 +1,7 @@
# Modelos de inicialização
Embora você possa começar com um projeto vazio e adicionar os pacotes necessários manualmente, os modelos de inicialização facilitam e são confortáveis para iniciar uma nova solução com a estrutura ABP. Clique no nome da lista abaixo para ver a documentação do modelo de inicialização relacionado:
- [**app**](Application.md) : modelo de aplicativo.
- [**módulo**](Module.md) : Módulo / modelo de serviço.

@ -0,0 +1,161 @@
# Modelo de inicialização do módulo MVC
Este modelo pode ser usado para criar um **módulo de aplicativo** **reutilizável com** base nas [melhores práticas e convenções de desenvolvimento do módulo](../Best-Practices/Index.md). Também é adequado para criar **microsserviços** (com ou sem interface do usuário).
## Como começar?
Você pode usar a [ABP CLI](../CLI.md) para criar um novo projeto usando este modelo de inicialização. Como alternativa, você pode criar e fazer o download diretamente na página [Introdução](https://abp.io/get-started) . A abordagem CLI é usada aqui.
Primeiro, instale a ABP CLI se você não tiver instalado antes:
```bash
dotnet tool install -g Volo.Abp.Cli
```
Em seguida, use o `abp new`comando em uma pasta vazia para criar uma nova solução:
```bash
abp new Acme.IssueManagement -t module
```
- `Acme.IssueManagement`é o nome da solução, como *YourCompany.YourProduct* . Você pode usar nomes de nível único, dois ou três níveis.
### Sem interface de usuário
O modelo vem com uma interface do usuário do MVC por padrão. Você pode usar a `--no-ui`opção para não incluir a camada da interface do usuário.
```bash
abp new Acme.IssueManagement -t mvc-module --no-ui
```
## Estrutura da solução
Com base nas opções especificadas, você obterá uma estrutura de solução ligeiramente diferente. Se você não especificar nenhuma opção, terá uma solução como a mostrada abaixo:
![issuemanagement-module-solution](../images/issuemanagement-module-solution.png)
Projetos são organizados como `src`, `test`e `host`pastas:
- `src`A pasta contém o módulo real, que é estratificado com base nos princípios [DDD](../Domain-Driven-Design.md) .
- `test` pasta contém testes de unidade e integração.
- `host`A pasta contém aplicativos com configurações diferentes para demonstrar como hospedar o módulo em um aplicativo. Isso não faz parte do módulo, mas é útil no desenvolvimento.
O diagrama abaixo mostra as camadas e dependências do projeto do módulo:
![módulo de dependências do projeto em camadas](../images/layered-project-dependencies-module.png)
Cada seção abaixo explicará o projeto relacionado e suas dependências.
### Projeto .Domain.Shared
Este projeto contém constantes, enumerações e outros objetos. Na verdade, eles fazem parte da camada de domínio, mas precisam ser usados por todas as camadas / projetos da solução.
Um `IssueType`enum e uma `IssueConsts`classe (que podem ter alguns campos constantes para a `Issue`entidade, como `MaxTitleLength`) são bons candidatos para este projeto.
- Este projeto não depende de outros projetos na solução. Todos os outros projetos dependem disso direta ou indiretamente.
### .Domain Project
Essa é a camada de domínio da solução. Ele contém principalmente [entidades, raízes agregadas](../Entities.md) , [serviços de domínio](../Domain-Services.md) , [tipos de valor](../Value-Types.md) , [interfaces de repositório](../Repositories.md) e outros objetos de domínio.
Uma `Issue`entidade, um `IssueManager`serviço de domínio e uma `IIssueRepository`interface são bons candidatos para este projeto.
- Depende do `.Domain.Shared`porque usa constantes, enumerações e outros objetos definidos nesse projeto.
### .Application.Contracts Project
Este projeto contém principalmente **interfaces de** [serviço de aplicativo](../Application-Services.md) e DTO ( [Data Transfer Objects](../Data-Transfer-Objects.md) ) da camada de aplicativo. Existe para separar a interface e a implementação da camada de aplicação. Dessa forma, o projeto de interface pode ser compartilhado com os clientes como um pacote de contrato.
Uma `IIssueAppService`interface e uma `IssueCreationDto`classe são boas candidatas para este projeto.
- Depende do `.Domain.Shared`porque ele pode usar constantes, enumerações e outros objetos compartilhados deste projeto nas interfaces de serviço de aplicativo e DTOs.
### Projeto de Aplicação
Este projeto contém as **implementações** de [serviço de aplicativo](../Application-Services.md) das interfaces definidas no projeto.`.Application.Contracts`
Uma `IssueAppService`turma é uma boa candidata para este projeto.
- Depende do `.Application.Contracts`projeto para poder implementar as interfaces e usar os DTOs.
- Depende do `.Domain`projeto para poder usar objetos de domínio (entidades, interfaces de repositório ... etc.) para executar a lógica do aplicativo.
### Projeto .EntityFrameworkCore
Este é o projeto de integração do EF Core. Ele define `DbContext`e implementa as interfaces de repositório definidas no `.Domain`projeto.
- Depende do `.Domain`projeto para poder fazer referência a entidades e interfaces de repositório.
> Você pode excluir este projeto se não desejar dar suporte ao EF Core para o seu módulo.
### Projeto .MongoDB
Este é o projeto de integração do MongoDB.
- Depende do `.Domain`projeto para poder fazer referência a entidades e interfaces de repositório.
> Você pode excluir este projeto se não quiser dar suporte ao MongoDB para o seu módulo.
### Projetos de teste
A solução possui vários projetos de teste, um para cada camada:
- `.Domain.Tests` é usado para testar a camada de domínio.
- `.Application.Tests` é usado para testar a camada de aplicativo.
- `.EntityFrameworkCore.Tests` é usado para testar a configuração do EF Core e os repositórios personalizados.
- `.MongoDB.Tests` é usado para testar a configuração do MongoDB e os repositórios personalizados.
- `.TestBase` é um projeto básico (compartilhado) para todos os testes.
Além disso, `.HttpApi.Client.ConsoleTestApp`é um aplicativo de console (não um projeto de teste automatizado) que demonstra o uso de APIs HTTP de um aplicativo Dotnet.
Projetos de teste são preparados para testes de integração;
- É totalmente integrado à estrutura ABP e a todos os serviços em sua aplicação.
- Ele usa o banco de dados SQLite na memória para o EF Core. Para o MongoDB, ele usa a biblioteca [Mongo2Go](https://github.com/Mongo2Go/Mongo2Go) .
- A autorização está desabilitada, portanto, qualquer serviço de aplicativo pode ser facilmente usado em testes.
Você ainda pode criar testes de unidade para suas classes, que serão mais difíceis de escrever (porque você precisará preparar objetos simulados / falsos), mas mais rápidos de executar (porque apenas testa uma única classe e ignora todo o processo de inicialização).
> Os testes de domínio e aplicativos estão usando o EF Core. Se você remover a integração do EF Core ou desejar usar o MongoDB para testar essas camadas, altere manualmente as referências do projeto e as dependências do módulo.
### Projetos Anfitriões
A solução possui alguns aplicativos host para executar seu módulo. Aplicativos host são usados para executar seu módulo em um aplicativo totalmente configurado. É útil no desenvolvimento. Os aplicativos host incluem alguns outros módulos além do módulo que está sendo desenvolvido:
Os aplicativos host oferecem suporte a dois tipos de cenários.
#### Cenário de aplicativo único (unificado)
Se o seu módulo tiver uma interface do usuário, o `.Web.Unified`aplicativo será usado para hospedar a interface do usuário e a API em um único ponto. Ele possui seu próprio `appsettings.json`arquivo (que inclui a cadeia de conexão do banco de dados) e as migrações do banco de dados EF Core.
Para o `.Web.Unified`aplicativo, há um único banco de dados chamado `YourProjectName_Unified`(como *IssueManagement_Unified* para esta amostra).
> Se você selecionou a `--no-ui`opção, este projeto não estará na sua solução.
##### Como correr?
Defina-o como o projeto de inicialização, execute o `Update-Database`comando para o EF Core no Package Manager Console e execute seu aplicativo. O nome de usuário padrão é `admin`e a senha é `1q2w3E*`.
#### Implantação separada e cenário de bancos de dados
Nesse cenário, há três aplicativos;
- `.IdentityServer`application é um servidor de autenticação usado por outros aplicativos. Ele possui seu próprio `appsettings.json`que contém conexão com o banco de dados e outras configurações.
- `.HttpApi.Host`hospeda a API HTTP do módulo. Ele possui seu próprio `appsettings.json`que contém conexões com o banco de dados e outras configurações.
- `.Web.Host`hospedar a interface do usuário do módulo. Este projeto contém um `appsettings.json`arquivo, mas não possui uma cadeia de conexão porque nunca se conecta ao banco de dados. Em vez disso, ele contém principalmente o terminal do servidor de API remoto e o servidor de autenticação.
O diagrama abaixo mostra a relação dos aplicativos:
![aplicativos de solução em camadas](../images/tiered-solution-applications.png)
`.Web.Host`O projeto usa a autenticação OpenId Connect para obter tokens de identidade e acesso para o usuário atual do `.IdentityServer`. Em seguida, usa o token de acesso para chamar o `.HttpApi.Host`. O servidor HTTP API usa autenticação de token de portador para obter declarações do token de acesso para autorizar o usuário atual.
##### Como correr?
Você deve executar o aplicativo com a ordem especificada:
- Primeiro, execute o `.IdentityServer`aplicativo, pois outros aplicativos dependem dele.
- Em seguida, execute o `.HttpApi.Host`que é usado pelo `.Web.Host`aplicativo.
- Por fim, você pode executar o `.Web.Host`projeto e efetuar login no aplicativo usando `admin`como nome de usuário e `1q2w3E*`senha.

@ -0,0 +1,3 @@
# Testing
Façam!

@ -0,0 +1,661 @@
## Tutorial Angular - Parte I
### Sobre este tutorial
Nesta série de tutoriais, você criará um aplicativo usado para gerenciar uma lista de livros e seus autores. **Angular** será usado como estrutura da interface do usuário e **MongoDB** será usado como provedor de banco de dados.
Esta é a primeira parte da série de tutoriais angulares. Veja todas as peças:
- **Parte I: Crie o projeto e uma página de lista de livros (este tutorial)**
- [Parte II: Criar, atualizar e excluir livros](Part-II)
- [Parte III: Testes de Integração](Part-III)
Você pode acessar o **código fonte** do aplicativo no [repositório GitHub](https://github.com/abpframework/abp/tree/dev/samples/BookStore-Angular-MongoDb) .
### Criando o projeto
Crie um novo projeto nomeado `Acme.BookStore`selecionando Angular como a estrutura da interface do usuário e MongoDB como o provedor de banco de dados, crie o banco de dados e execute o aplicativo seguindo o [documento Introdução](../../Getting-Started-Angular-Template.md) .
### Estrutura da solução (back-end)
É assim que a estrutura da solução em camadas cuida da criação:
![solução de back-end da livraria](images/bookstore-backend-solution-v2.png)
> Você pode ver o [documento do modelo de aplicativo](../../Startup-Templates/Application.md) para entender a estrutura da solução em detalhes. No entanto, você entenderá o básico com este tutorial.
### Criar a entidade do livro
A camada de domínio no modelo de inicialização é separada em dois projetos:
- `Acme.BookStore.Domain`contém suas [entidades](../../Entities.md) , [serviços de domínio](../../Domain-Services.md) e outros objetos principais de domínio.
- `Acme.BookStore.Domain.Shared` contém constantes, enumerações ou outros objetos relacionados ao domínio que podem ser compartilhados com os clientes.
Defina [entidades](../../Entities.md) na **camada de domínio** ( `Acme.BookStore.Domain`projeto) da solução. A entidade principal do aplicativo é a `Book`. Crie uma classe, chamada `Book`, no `Acme.BookStore.Domain`projeto, como mostrado abaixo:
```csharp
using System;
using Volo.Abp.Domain.Entities.Auditing;
namespace Acme.BookStore
{
public class Book : AuditedAggregateRoot<Guid>
{
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
}
```
- O ABP possui duas classes base fundamentais para entidades: `AggregateRoot`e `Entity`. **A raiz agregada** é um dos conceitos de **DDD (Domain Driven Design)** . Consulte o [documento da entidade](../../Entities.md) para obter detalhes e melhores práticas.
- `Book`entidade herda `AuditedAggregateRoot`que adiciona algumas propriedades de auditoria ( `CreationTime`, `CreatorId`, `LastModificationTime`... etc.) no topo da `AggregateRoot`classe.
- `Guid`é o **tipo** de **chave primária** da `Book`entidade.
#### BookType Enum
Defina a `BookType`enumeração no `Acme.BookStore.Domain.Shared`projeto:
```csharp
namespace Acme.BookStore
{
public enum BookType
{
Undefined,
Adventure,
Biography,
Dystopia,
Fantastic,
Horror,
Science,
ScienceFiction,
Poetry
}
}
```
#### Adicionar entidade de livro ao seu DbContext
Adicione uma `IMongoCollection`propriedade ao `BookStoreMongoDbContext`interior do `Acme.BookStore.MongoDB`projeto:
```csharp
public class BookStoreMongoDbContext : AbpMongoDbContext
{
public IMongoCollection<Book> Books => Collection<Book>();
...
}
```
#### Adicionar dados de semente (amostra)
Esta seção é opcional, mas seria bom ter um dado inicial no banco de dados na primeira execução. O ABP fornece um [sistema de semente de dados](../../Data-Seeding.md) . Crie uma classe derivada de `IDataSeedContributor`no `.Domain`projeto:
```csharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore
{
public class BookStoreDataSeederContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Book, Guid> _bookRepository;
public BookStoreDataSeederContributor(IRepository<Book, Guid> bookRepository)
{
_bookRepository = bookRepository;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() > 0)
{
return;
}
await _bookRepository.InsertAsync(
new Book
{
Name = "1984",
Type = BookType.Dystopia,
PublishDate = new DateTime(1949, 6, 8),
Price = 19.84f
}
);
await _bookRepository.InsertAsync(
new Book
{
Name = "The Hitchhiker's Guide to the Galaxy",
Type = BookType.ScienceFiction,
PublishDate = new DateTime(1995, 9, 27),
Price = 42.0f
}
);
}
}
}
```
`BookStoreDataSeederContributor`simplesmente insere dois livros no banco de dados se não houver nenhum livro adicionado antes. O ABP descobre e executa automaticamente essa classe quando você propaga o banco de dados executando o `Acme.BookStore.DbMigrator`projeto.
### Crie o serviço de aplicativo
O próximo passo é criar um [serviço de aplicativo](../../Application-Services.md) para gerenciar (criar, listar, atualizar, excluir ...) os livros. A camada de aplicativo no modelo de inicialização é separada em dois projetos:
- `Acme.BookStore.Application.Contracts` contém principalmente seus DTOs e interfaces de serviço de aplicativo.
- `Acme.BookStore.Application` contém as implementações dos seus serviços de aplicativo.
#### BookDto
Crie uma classe DTO denominada `BookDto`no `Acme.BookStore.Application.Contracts`projeto:
```csharp
using System;
using Volo.Abp.Application.Dtos;
namespace Acme.BookStore
{
public class BookDto : AuditedEntityDto<Guid>
{
public string Name { get; set; }
public BookType Type { get; set; }
public DateTime PublishDate { get; set; }
public float Price { get; set; }
}
}
```
- **As** classes **DTO** são usadas para **transferir dados** entre a *camada de apresentação* e a *camada de aplicativo* . Consulte o [documento Objetos de transferência de dados](../../Data-Transfer-Objects.md) para obter mais detalhes.
- `BookDto` é usado para transferir dados do livro para a camada de apresentação para mostrar as informações do livro na interface do usuário.
- `BookDto`é derivado do `AuditedEntityDto<Guid>`que possui propriedades de auditoria exatamente como a `Book`classe definida acima.
Será necessário converter `Book`entidades em `BookDto`objetos enquanto retorna os livros para a camada de apresentação. A biblioteca do [AutoMapper](https://automapper.org/) pode automatizar essa conversão quando você define o mapeamento adequado. O modelo de inicialização é fornecido com o AutoMapper configurado, para que você possa definir o mapeamento na `BookStoreApplicationAutoMapperProfile`classe no `Acme.BookStore.Application`projeto:
```csharp
using AutoMapper;
namespace Acme.BookStore
{
public class BookStoreApplicationAutoMapperProfile : Profile
{
public BookStoreApplicationAutoMapperProfile()
{
CreateMap<Book, BookDto>();
}
}
}
```
#### CreateUpdateBookDto
Crie uma classe DTO denominada `CreateUpdateBookDto`no `Acme.BookStore.Application.Contracts`projeto:
```csharp
using System;
using System.ComponentModel.DataAnnotations;
namespace Acme.BookStore
{
public class CreateUpdateBookDto
{
[Required]
[StringLength(128)]
public string Name { get; set; }
[Required]
public BookType Type { get; set; } = BookType.Undefined;
[Required]
public DateTime PublishDate { get; set; }
[Required]
public float Price { get; set; }
}
}
```
- Essa classe DTO é usada para obter informações do livro a partir da interface do usuário ao criar ou atualizar um livro.
- Ele define atributos de anotação de dados (como `[Required]`) para definir validações para as propriedades. Os DTOs são [validados automaticamente](../../Validation.md) pela estrutura ABP.
Em seguida, adicione um mapeamento `BookStoreApplicationAutoMapperProfile`do `CreateUpdateBookDto`objeto à `Book`entidade:
```csharp
CreateMap<CreateUpdateBookDto, Book>();
```
#### IBookAppService
Defina uma interface nomeada `IBookAppService`no `Acme.BookStore.Application.Contracts`projeto:
```csharp
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
namespace Acme.BookStore
{
public interface IBookAppService :
ICrudAppService< //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
{
}
}
```
- A definição de interfaces para serviços de aplicativos não é requerida pela estrutura. No entanto, é sugerido como uma prática recomendada.
- `ICrudAppService`define comuns **CRUD** métodos: `GetAsync`, `GetListAsync`, `CreateAsync`, `UpdateAsync`e `DeleteAsync`. Não é necessário estendê-lo. Em vez disso, você pode herdar da `IApplicationService`interface vazia e definir seus próprios métodos manualmente.
- Existem algumas variações de `ICrudAppService`onde você pode usar DTOs separados para cada método.
#### BookAppService
Implemente `IBookAppService`como nomeado `BookAppService`no `Acme.BookStore.Application`projeto:
```csharp
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore
{
public class BookAppService :
CrudAppService<Book, BookDto, Guid, PagedAndSortedResultRequestDto,
CreateUpdateBookDto, CreateUpdateBookDto>,
IBookAppService
{
public BookAppService(IRepository<Book, Guid> repository)
: base(repository)
{
}
}
}
```
- `BookAppService`é derivado do `CrudAppService<...>`qual implementa todos os métodos CRUD definidos acima.
- `BookAppService`injeta `IRepository<Book, Guid>`qual é o repositório padrão da `Book`entidade. O ABP cria automaticamente repositórios padrão para cada raiz (ou entidade) agregada. Veja o [documento do repositório](../../Repositories) .
- `BookAppService`usa `IObjectMapper`para converter `Book`objetos em `BookDto`objetos e `CreateUpdateBookDto`objetos em `Book`objetos. O modelo de inicialização usa a biblioteca [AutoMapper](http://automapper.org/) como o provedor de mapeamento de objetos. Você definiu os mapeamentos antes, para que funcionem conforme o esperado.
### Controladores de API automática
Você normalmente cria **controladores** para expor serviços de aplicativos como pontos de extremidade da **API HTTP** . Assim, permite que navegadores ou clientes de terceiros os chamem via AJAX. O ABP pode configurar [**automaticamente**](../../AspNetCore/Auto-API-Controllers.md) seus serviços de aplicativo como controladores de API MVC por convenção.
#### UI do Swagger
O modelo de inicialização está configurado para executar a [interface do usuário do swagger](https://swagger.io/tools/swagger-ui/) usando a biblioteca [Swashbuckle.AspNetCore](https://github.com/domaindrivendev/Swashbuckle.AspNetCore) . Execute o `Acme.BookStore.HttpApi.Host`aplicativo e insira `https://localhost:XXXX/swagger/`(substitua XXXX por sua própria porta) como URL no seu navegador.
Você verá alguns pontos de extremidade de serviço internos, bem como o `Book`serviço e seus pontos de extremidade no estilo REST:
![livraria-arrogância](images/bookstore-swagger-api.png)
O Swagger tem uma ótima interface para testar APIs. Você pode tentar executar a `[GET] /api/app/book`API para obter uma lista de livros.
### Crie a página de livros
Neste tutorial;
- [A CLI angular](https://angular.io/cli) será usada para criar módulos, componentes e serviços
- [NGXS](https://ngxs.gitbook.io/ngxs/) será usado como a biblioteca de gerenciamento de estado
- [O Bootstrap](https://ng-bootstrap.github.io/#/home) será usado como a biblioteca de componentes da interface do usuário.
- [O Visual Studio Code](https://code.visualstudio.com/) será usado como editor de código (você pode usar seu editor favorito).
#### Instalar pacotes NPM
Abra uma janela do terminal, vá para a `angular`pasta e execute o `yarn` comando para instalar os pacotes NPM:
```
yarn
```
#### BooksModule
Execute a seguinte linha de comando para criar um novo módulo, denominado `BooksModule`:
```bash
yarn ng generate module books --route books --module app.module
```
![Creating-Books-Module.terminal](images/bookstore-creating-books-module-terminal.png)
Execute `yarn start`, aguarde Angular para executar o aplicativo e abra `http://localhost:4200/books`em um navegador:
![página inicial dos livros](images/bookstore-initial-books-page.png)
#### Encaminhamento
Abra `app-routing.module.ts`e substitua `books`conforme mostrado abaixo:
```js
import { ApplicationLayoutComponent } from '@abp/ng.theme.basic';-
//...
{
path: 'books',
component: ApplicationLayoutComponent,
loadChildren: () => import('./books/books.module').then(m => m.BooksModule),
data: {
routes: {
name: 'Books',
} as ABP.Route,
},
},
```
`ApplicationLayoutComponent`configuração define o layout do aplicativo para a nova página. Se você deseja ver sua rota na barra de navegação (menu principal), também deve adicionar o `data`objeto com `name`propriedade à sua rota.
![página inicial dos livros](images/bookstore-initial-books-page-with-layout.png)
#### Componente da lista de livros
Primeiro, substitua pela `books.component.html`seguinte linha para colocar a saída do roteador:
```html
<router-outlet></router-outlet>
```
Em seguida, execute o comando abaixo no terminal na pasta raiz para gerar um novo componente, chamado book-list:
```bash
yarn ng generate component books/book-list
```
![terminal-criando-lista-de-livros](images/bookstore-creating-book-list-terminal.png)
Importe `SharedModule`para `BooksModule`para reutilizar alguns componentes e serviços definidos em:
```js
import { SharedModule } from '../shared/shared.module';
@NgModule({
//...
imports: [
//...
SharedModule,
],
})
export class BooksModule {}
```
Em seguida, atualize o `routes`no `books-routing.module.ts`para adicionar o novo componente book-list:
```js
import { BookListComponent } from './book-list/book-list.component';
const routes: Routes = [
{
path: '',
component: BooksComponent,
children: [{ path: '', component: BookListComponent }],
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class BooksRoutingModule {}
```
![página inicial da lista de livros](images/bookstore-initial-book-list-page.png)
#### Criar BooksState
Execute o seguinte comando no terminal para criar um novo estado, denominado `BooksState`:
```shell
yarn ng generate ngxs-schematic:state books
```
Este comando cria vários novos arquivos e edições `app.modules.ts`para importar o `NgxsModule`com o novo estado:
```js
// app.module.ts
import { BooksState } from './store/states/books.state';
@NgModule({
imports: [
//...
NgxsModule.forRoot([BooksState]),
],
//...
})
export class AppModule {}
```
#### Obter dados de livros do back-end
Primeiro, crie tipos de dados para mapear os dados que retornam do back-end (você pode verificar a interface do swagger ou a API do back-end para conhecer o formato dos dados).
Modifique o `books.ts`como mostrado abaixo:
```js
export namespace Books {
export interface State {
books: Response;
}
export interface Response {
items: Book[];
totalCount: number;
}
export interface Book {
name: string;
type: BookType;
publishDate: string;
price: number;
lastModificationTime: string;
lastModifierId: string;
creationTime: string;
creatorId: string;
id: string;
}
export enum BookType {
Undefined,
Adventure,
Biography,
Dystopia,
Fantastic,
Horror,
Science,
ScienceFiction,
Poetry,
}
}
```
Adicionada `Book`interface que representa um objeto de livro e `BookType`enum representa uma categoria de livro.
#### BooksService
Agora, crie um novo serviço, nomeado `BooksService`para executar chamadas HTTP para o servidor:
```bash
yarn ng generate service books/shared/books
```
![serviço-terminal-saída](images/bookstore-service-terminal-output.png)
Modifique `books.service.ts`como mostrado abaixo:
```js
import { Injectable } from '@angular/core';
import { RestService } from '@abp/ng.core';
import { Books } from '../../store/models';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class BooksService {
constructor(private restService: RestService) {}
get(): Observable<Books.Response> {
return this.restService.request<void, Books.Response>({
method: 'GET',
url: '/api/app/book'
});
}
}
```
Adicionado o `get`método para obter a lista de livros executando uma solicitação HTTP no terminal relacionado.
Substitua o `books.actions.ts`conteúdo conforme mostrado abaixo:
```js
export class GetBooks {
static readonly type = '[Books] Get';
}
```
#### Implementar o BooksState
Abra o `books.state.ts`e altere o arquivo, como mostrado abaixo:
```js
import { State, Action, StateContext, Selector } from '@ngxs/store';
import { GetBooks } from '../actions/books.actions';
import { Books } from '../models/books';
import { BooksService } from '../../books/shared/books.service';
import { tap } from 'rxjs/operators';
@State<Books.State>({
name: 'BooksState',
defaults: { books: {} } as Books.State,
})
export class BooksState {
@Selector()
static getBooks(state: Books.State) {
return state.books.items || [];
}
constructor(private booksService: BooksService) {}
@Action(GetBooks)
get(ctx: StateContext<Books.State>) {
return this.booksService.get().pipe(
tap(booksResponse => {
ctx.patchState({
books: booksResponse,
});
}),
);
}
}
```
Adicionada a `GetBooks`ação que usa o `BookService`definido acima para obter os livros e corrigir o estado.
> O NGXS exige retornar o observável sem assiná-lo, conforme feito nesta amostra (na função get).
#### BookListComponent
Modifique o `book-list.component.ts`como mostrado abaixo:
```js
import { Component, OnInit } from '@angular/core';
import { Store, Select } from '@ngxs/store';
import { BooksState } from '../../store/states';
import { Observable } from 'rxjs';
import { Books } from '../../store/models';
import { GetBooks } from '../../store/actions';
@Component({
selector: 'app-book-list',
templateUrl: './book-list.component.html',
styleUrls: ['./book-list.component.scss'],
})
export class BookListComponent implements OnInit {
@Select(BooksState.getBooks)
books$: Observable<Books.Book[]>;
booksType = Books.BookType;
loading = false;
constructor(private store: Store) {}
ngOnInit() {
this.loading = true;
this.store.dispatch(new GetBooks()).subscribe(() => {
this.loading = false;
});
}
}
```
> Consulte as [ações de despacho](https://ngxs.gitbook.io/ngxs/concepts/store#dispatching-actions) e [selecione](https://ngxs.gitbook.io/ngxs/concepts/select) na documentação do NGXS para obter mais informações sobre esses recursos do NGXS.
Substitua o `book-list.component.html`conteúdo conforme mostrado abaixo:
```html
<div id="wrapper" class="card">
<div class="card-header">
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">
Books
</h5>
</div>
</div>
</div>
<div class="card-body">
<p-table [value]="books$ | async" [loading]="loading" [paginator]="true" [rows]="10">
<ng-template pTemplate="header">
<tr>
<th>Book name</th>
<th>Book type</th>
<th>Publish date</th>
<th>Price</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-data>
<tr>
<td>{{ data.name }}</td>
<td>{{ booksType[data.type] }}</td>
<td>{{ data.publishDate | date }}</td>
<td>{{ data.price }}</td>
</tr>
</ng-template>
</p-table>
</div>
</div>
```
> Usamos a [tabela PrimeNG](https://www.primefaces.org/primeng/#/table) neste componente.
A página de livros resultante é mostrada abaixo:
![livraria-lista-de-livros](images/bookstore-book-list.png)
E esta é a estrutura de pastas e arquivos no final deste tutorial:
![img](images/bookstore-angular-file-tree.png)
> Este tutorial segue o [Guia de estilo angular](https://angular.io/guide/styleguide#file-tree) .
### Próxima parte
Veja a [próxima parte](Part-II.md) deste tutorial.

@ -0,0 +1,582 @@
## Tutorial Angular - Parte II
### Sobre este tutorial
Esta é a segunda parte da série de tutoriais angulares. Veja todas as peças:
- [Parte I: Crie o projeto e uma página da lista de livros](Part-I.md)
- **Parte II: Criar, atualizar e excluir livros (este tutorial)**
- [Parte III: Testes de Integração](Part-III.md)
Você pode acessar o **código fonte** do aplicativo no [repositório GitHub](https://github.com/abpframework/abp/tree/dev/samples/BookStore-Angular-MongoDb) .
### Criando um novo livro
Nesta seção, você aprenderá como criar um novo formulário de diálogo modal para criar um novo livro.
#### Definição do tipo
Criar uma interface, com o nome `CreateUpdateBookInput`no `books.ts`como mostrado abaixo:
```js
export namespace Books {
//...
export interface CreateUpdateBookInput {
name: string;
type: BookType;
publishDate: string;
price: number;
}
}
```
`CreateUpdateBookInput`interface corresponde ao `CreateUpdateBookDto`no back-end.
#### Método de Serviço
Abra o `books.service.ts`e adicione um novo método, nomeado `create`para executar uma solicitação HTTP POST no servidor:
```js
create(createBookInput: Books.CreateUpdateBookInput): Observable<Books.Book> {
return this.restService.request<Books.CreateUpdateBookInput, Books.Book>({
method: 'POST',
url: '/api/app/book',
body: createBookInput
});
}
```
- `restService.request`A função obtém parâmetros genéricos para os tipos enviados e recebidos do servidor. Este exemplo envia um `CreateUpdateBookInput`objeto e recebe um `Book`objeto (você pode definir o tipo `void`de solicitação ou retorno, se não for usado).
#### Definições de estado
Adicione a `CreateUpdateBook`ação ao `books.actions.ts`conforme mostrado abaixo:
```js
import { Books } from '../models';
export class CreateUpdateBook {
static readonly type = '[Books] Create Update Book';
constructor(public payload: Books.CreateUpdateBookInput) {}
}
```
Abra `books.state.ts`e defina o `save`método que ouvirá uma `CreateUpdateBook`ação para criar um livro:
```js
import { ... , CreateUpdateBook } from '../actions/books.actions';
import { ... , switchMap } from 'rxjs/operators';
//...
@Action(CreateUpdateBook)
save(ctx: StateContext<Books.State>, action: CreateUpdateBook) {
return this.booksService
.create(action.payload)
.pipe(switchMap(() => ctx.dispatch(new GetBooks())));
}
```
Quando a `SaveBook`ação é despachada, o método save é executado. Ele chama o `create`método do `BooksService`definido anteriormente. Após a chamada de serviço, `BooksState`despacha a `GetBooks`ação para obter livros novamente do servidor para atualizar a página.
#### Adicionar um modal ao BookListComponent
Abra o `book-list.component.html`e adicione o `abp-modal`para mostrar / ocultar o modal para criar um novo livro.
```html
<abp-modal [(visible)]="isModalOpen">
<ng-template #abpHeader>
<h3>New Book</h3>
</ng-template>
<ng-template #abpBody> </ng-template>
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
Cancel
</button>
</ng-template>
</abp-modal>
```
`abp-modal`é um componente pré-construído para mostrar os modais. Embora você possa usar outra abordagem para mostrar um modal, `abp-modal`fornece benefícios adicionais.
Adicione um botão rotulado `New book`para mostrar o modal:
```html
<div class="row">
<div class="col col-md-6">
<h5 class="card-title">
Books
</h5>
</div>
<div class="text-right col col-md-6">
<button id="create-role" class="btn btn-primary" type="button" (click)="createBook()">
<i class="fa fa-plus mr-1"></i> <span>New book</span>
</button>
</div>
</div>
```
Abra a variável `book-list.component.ts`e adicione `isModalOpen`e `createBook`método para mostrar / ocultar o modal.
```js
isModalOpen = false;
//...
createBook() {
this.isModalOpen = true;
}
```
![modal vazio](images/bookstore-empty-new-book-modal.png)
#### Criar um formulário reativo
> [Os formulários reativos](https://angular.io/guide/reactive-forms) fornecem uma abordagem orientada a modelo para lidar com entradas de formulário cujos valores mudam ao longo do tempo.
Adicione uma `form`variável e injete um `FormBuilder`serviço `book-list.component.ts`como mostrado abaixo (lembre-se de adicionar a instrução de importação).
```js
import { FormGroup, FormBuilder } from '@angular/forms';
form: FormGroup;
constructor(
//...
private fb: FormBuilder
) {}
```
> O serviço [FormBuilder](https://angular.io/api/forms/FormBuilder) fornece métodos convenientes para gerar controles. Reduz a quantidade de clichê necessária para criar formulários complexos.
Adicione o `buildForm`método para criar um formulário de livro.
```js
buildForm() {
this.form = this.fb.group({
name: ['', Validators.required],
type: [null, Validators.required],
publishDate: [null, Validators.required],
price: [null, Validators.required],
});
}
```
- O `group`método de `FormBuilder`( `fb`) cria a `FormGroup`.
- Adicionado `Validators.required`método estático que valida o elemento de formulário relacionado.
Modifique o `createBook`método como mostrado abaixo:
```js
createBook() {
this.buildForm();
this.isModalOpen = true;
}
```
#### Crie os elementos DOM do formulário
Abra `book-list.component.html`e adicione o formulário no modelo de corpo do modal.
```html
<ng-template #abpBody>
<form [formGroup]="form">
<div class="form-group">
<label for="book-name">Name</label><span> * </span>
<input type="text" id="book-name" class="form-control" formControlName="name" autofocus />
</div>
<div class="form-group">
<label for="book-price">Price</label><span> * </span>
<input type="number" id="book-price" class="form-control" formControlName="price" />
</div>
<div class="form-group">
<label for="book-type">Type</label><span> * </span>
<select class="form-control" id="book-type" formControlName="type">
<option [ngValue]="null">Select a book type</option>
<option [ngValue]="booksType[type]" *ngFor="let type of bookTypeArr"> {{ type }}</option>
</select>
</div>
<div class="form-group">
<label>Publish date</label><span> * </span>
<input
#datepicker="ngbDatepicker"
class="form-control"
name="datepicker"
formControlName="publishDate"
ngbDatepicker
(click)="datepicker.toggle()"
/>
</div>
</form>
</ng-template>
```
- Este modelo cria um formulário com os campos Nome, Preço, Tipo e Data de publicação.
> Usamos o [datepicker do NgBootstrap](https://ng-bootstrap.github.io/#/components/datepicker/overview) neste componente.
Abra o `book-list.component.ts`e crie uma matriz chamada `bookTypeArr`:
```js
//...
form: FormGroup;
bookTypeArr = Object.keys(Books.BookType).filter(
bookType => typeof this.booksType[bookType] === 'number'
);
```
O `bookTypeArr`contém os campos da `BookType`enumeração. A matriz resultante é mostrada abaixo:
```js
['Adventure', 'Biography', 'Dystopia', 'Fantastic' ...]
```
Essa matriz foi usada no modelo de formulário anterior (no `ngFor`loop).
#### Requisitos do Datepicker
Você precisa importar `NgbDatepickerModule`para o `books.module.ts`:
```js
import { NgbDatepickerModule } from '@ng-bootstrap/ng-bootstrap';
@NgModule({
imports: [
// ...
NgbDatepickerModule,
],
})
export class BooksModule {}
```
Abra o `book-list.component.ts`e adicione `providers`como mostrado abaixo:
```js
import { NgbDateNativeAdapter, NgbDateAdapter } from '@ng-bootstrap/ng-bootstrap';
@Component({
// ...
providers: [{ provide: NgbDateAdapter, useClass: NgbDateNativeAdapter }],
})
export class BookListComponent implements OnInit {
// ...
```
> O `NgbDateAdapter`valor do Datepicker converte em `Date`tipo. Consulte os [adaptadores datepicker](https://ng-bootstrap.github.io/#/components/datepicker/overview) para obter mais detalhes.
![forma de livro novo](images/bookstore-new-book-form.png)
#### Salvando o livro
Abra o `book-list.component.html`e adicione um `abp-button`para salvar o formulário.
```html
<ng-template #abpFooter>
<button type="button" class="btn btn-secondary" #abpClose>
Cancel
</button>
<button class="btn btn-primary" (click)="save()">
<i class="fa fa-check mr-1"></i>
Save
</button>
</ng-template>
```
Isso adiciona um botão Salvar à área inferior do modal:
![livraria-novo-livro-formulário-v2](images/bookstore-new-book-form-v2.png)
Em seguida, defina um `save`método no `BookListComponent`:
```js
save() {
if (this.form.invalid) {
return;
}
this.store.dispatch(new CreateUpdateBook(this.form.value)).subscribe(() => {
this.isModalOpen = false;
this.form.reset();
});
}
```
### Atualizando um livro existente
#### BooksService
Abra o `books.service.ts`e adicione os métodos `getById`e `update`.
```js
getById(id: string): Observable<Books.Book> {
return this.restService.request<void, Books.Book>({
method: 'GET',
url: `/api/app/book/${id}`
});
}
update(updateBookInput: Books.CreateUpdateBookInput, id: string): Observable<Books.Book> {
return this.restService.request<Books.CreateUpdateBookInput, Books.Book>({
method: 'PUT',
url: `/api/app/book/${id}`,
body: updateBookInput
});
}
```
#### Ação CreateUpdateBook
Abra o parâmetro `books.actins.ts`e adicione `id`à `CreateUpdateBook`ação:
```js
export class CreateUpdateBook {
static readonly type = '[Books] Create Update Book';
constructor(public payload: Books.CreateUpdateBookInput, public id?: string) {}
}
```
Abra `books.state.ts`e modifique o `save`método conforme mostrado abaixo:
```js
@Action(CreateUpdateBook)
save(ctx: StateContext<Books.State>, action: CreateUpdateBook) {
let request;
if (action.id) {
request = this.booksService.update(action.payload, action.id);
} else {
request = this.booksService.create(action.payload);
}
return request.pipe(switchMap(() => ctx.dispatch(new GetBooks())));
}
```
#### BookListComponent
Injectar `BooksService`dependência, adicionando-o ao `book-list.component.ts`construtor e adicione uma variável chamada `selectedBook`.
```js
import { BooksService } from '../shared/books.service';
//...
selectedBook = {} as Books.Book;
constructor(
//...
private booksService: BooksService
)
```
`booksService`é usado para obter o livro de edição para preparar o formulário. Modifique o `buildForm`método para reutilizar o mesmo formulário ao editar um livro.
```js
buildForm() {
this.form = this.fb.group({
name: [this.selectedBook.name || '', Validators.required],
type: this.selectedBook.type || null,
publishDate: this.selectedBook.publishDate ? new Date(this.selectedBook.publishDate) : null,
price: this.selectedBook.price || null,
});
}
```
Adicione o `editBook`método como mostrado abaixo:
```js
editBook(id: string) {
this.booksService.getById(id).subscribe(book => {
this.selectedBook = book;
this.buildForm();
this.isModalOpen = true;
});
}
```
Adicionado `editBook`método para obter o livro de edição, criar o formulário e mostrar o modal.
Agora, adicione a `selectedBook`definição ao `createBook`método para reutilizar o mesmo formulário ao criar um novo livro:
```js
createBook() {
this.selectedBook = {} as Books.Book;
//...
}
```
Modifique o `save`método para passar o ID do livro selecionado, como mostrado abaixo:
```js
save() {
if (this.form.invalid) {
return;
}
this.store.dispatch(new CreateUpdateBook(this.form.value, this.selectedBook.id))
.subscribe(() => {
this.isModalOpen = false;
this.form.reset();
});
}
```
#### Adicione o menu suspenso "Ações" à tabela
Abra o `book-list.component.html` e adicione modifique o `p-table` como mostrado abaixo:
```html
<p-table [value]="books$ | async" [loading]="loading" [paginator]="true" [rows]="10">
<ng-template pTemplate="header">
<tr>
<th>Actions</th>
<th>Book name</th>
<th>Book type</th>
<th>Publish date</th>
<th>Price</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-data>
<tr>
<td>
<div ngbDropdown class="d-inline-block">
<button
class="btn btn-primary btn-sm dropdown-toggle"
data-toggle="dropdown"
aria-haspopup="true"
ngbDropdownToggle
>
<i class="fa fa-cog mr-1"></i>Actions
</button>
<div ngbDropdownMenu>
<button ngbDropdownItem (click)="editBook(data.id)">Edit</button>
</div>
</div>
</td>
<td>{{ data.name }}</td>
<td>{{ booksType[data.type] }}</td>
<td>{{ data.publishDate | date }}</td>
<td>{{ data.price }}</td>
</tr>
</ng-template>
</p-table>
```
- Adicionado um `th`para a coluna "Ações".
- Adicionado `button`com `ngbDropdownToggle`para abrir ações quando clicamos no botão.
> Nós costumávamos usar o [NgbDropdown](https://ng-bootstrap.github.io/#/components/dropdown/examples) no menu suspenso de ações.
A interface do usuário final é semelhante a:
![botões de ações](https://raw.githubusercontent.com/abpframework/abp/master/docs/en/Tutorials/Angular/images/bookstore-actions-buttons.png)
Atualize o cabeçalho modal para alterar o título com base na operação atual:
```html
<ng-template #abpHeader>
<h3>{{ selectedBook.id ? 'Edit' : 'New Book' }}</h3>
</ng-template>
```
![botões de ações](images/bookstore-edit-modal.png)
### Exclusão de um livro existente
#### BooksService
Abra `books.service.ts`e inclua um `delete`método para excluir um livro com o `id`, executando uma solicitação HTTP no nó de extremidade relacionado:
```js
delete(id: string): Observable<void> {
return this.restService.request<void, void>({
method: 'DELETE',
url: `/api/app/book/${id}`
});
}
```
#### Ação DeleteBook
Adicione uma ação chamada `DeleteBook`para `books.actions.ts`:
```js
export class DeleteBook {
static readonly type = '[Books] Delete';
constructor(public id: string) {}
}
```
Abra o `books.state.ts`e adicione o `delete`método que ouvirá a `DeleteBook`ação para excluir um livro:
```js
import { ... , DeleteBook } from '../actions/books.actions';
//...
@Action(DeleteBook)
delete(ctx: StateContext<Books.State>, action: DeleteBook) {
return this.booksService.delete(action.id).pipe(switchMap(() => ctx.dispatch(new GetBooks())));
}
```
- Adicionado `DeleteBook`à lista de importação.
- Usa `bookService`para excluir o livro.
\#### Adicionar um botão Excluir
Abra `book-list.component.html`e modifique `ngbDropdownMenu`para adicionar o botão excluir, como mostrado abaixo:
```html
<div ngbDropdownMenu>
...
<button ngbDropdownItem (click)="delete(data.id, data.name)">
Delete
</button>
</div>
```
A interface do usuário suspensa de ações finais é semelhante a abaixo:
![livraria-final-ações-suspensa](images/bookstore-final-actions-dropdown.png)
\#### Caixa de diálogo Excluir confirmação
Abra `book-list.component.ts`e injete o `ConfirmationService`.
```js
import { ConfirmationService } from '@abp/ng.theme.shared';
//...
constructor(
//...
private confirmationService: ConfirmationService
)
```
> `ConfirmationService` é um serviço simples fornecido pela estrutura ABP que usa internamente o PrimeNG.
Adicione um método de exclusão ao `BookListComponent`:
```js
import { ... , DeleteBook } from '../../store/actions';
import { ... , Toaster } from '@abp/ng.theme.shared';
//...
delete(id: string, name: string) {
this.confirmationService
.error(`${name} will be deleted. Do you confirm that?`, 'Are you sure?')
.subscribe(status => {
if (status === Toaster.Status.confirm) {
this.store.dispatch(new DeleteBook(id));
}
});
}
```
O `delete`método mostra um pop-up de confirmação e assina a resposta do usuário. `DeleteBook`ação despachada somente se o usuário clicar no `Yes`botão O pop-up de confirmação é exibido abaixo:
![livraria-confirmação-pop-up](images/bookstore-confirmation-popup.png)
### Próxima parte
Veja a [próxima parte](Part-III.md) deste tutorial.

@ -0,0 +1,181 @@
## Tutorial do ASP.NET Core MVC - Parte III
### Sobre este tutorial
Esta é a terceira parte da série de tutoriais Angular. Veja todas as peças:
- [Parte I: Crie o projeto e uma página da lista de livros](Part-I.md)
- [Parte II: Criar, atualizar e excluir livros](Part-II.md)
- **Parte III: Testes de Integração (este tutorial)**
Esta parte abrange os testes do **lado** do **servidor** . Você pode acessar o **código fonte** do aplicativo no [repositório GitHub](https://github.com/abpframework/abp/tree/dev/samples/BookStore-Angular-MongoDb) .
### Testar projetos na solução
Existem vários projetos de teste na solução:
![livraria-teste-projetos](images/bookstore-test-projects-v3.png)
Cada projeto é usado para testar o projeto de aplicativo relacionado. Os projetos de teste usam as seguintes bibliotecas para teste:
- [xunit](https://xunit.github.io/) como a principal estrutura de teste.
- [Shouldly](http://shouldly.readthedocs.io/en/latest/) como uma biblioteca de asserções.
- [NSubstitute](http://nsubstitute.github.io/) como uma biblioteca de zombaria.
### Adicionando dados de teste
O modelo de inicialização contém a `BookStoreTestDataSeedContributor`classe no `Acme.BookStore.TestBase`projeto que cria alguns dados para executar os testes.
Mude a `BookStoreTestDataSeedContributor`classe como mostrado abaixo:
```csharp
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
namespace Acme.BookStore
{
public class BookStoreTestDataSeedContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Book, Guid> _bookRepository;
private readonly IGuidGenerator _guidGenerator;
public BookStoreTestDataSeedContributor(
IRepository<Book, Guid> bookRepository,
IGuidGenerator guidGenerator)
{
_bookRepository = bookRepository;
_guidGenerator = guidGenerator;
}
public async Task SeedAsync(DataSeedContext context)
{
await _bookRepository.InsertAsync(
new Book
{
Id = _guidGenerator.Create(),
Name = "Test book 1",
Type = BookType.Fantastic,
PublishDate = new DateTime(2015, 05, 24),
Price = 21
}
);
await _bookRepository.InsertAsync(
new Book
{
Id = _guidGenerator.Create(),
Name = "Test book 2",
Type = BookType.Science,
PublishDate = new DateTime(2014, 02, 11),
Price = 15
}
);
}
}
}
```
- Injetado `IRepository<Book, Guid>`e usado no `SeedAsync`para criar duas entidades de livro como dados de teste.
- `IGuidGenerator`Serviço usado para criar GUIDs. Embora `Guid.NewGuid()`funcionasse perfeitamente para testes, `IGuidGenerator`possui recursos adicionais especialmente importantes ao usar bancos de dados reais (consulte o documento de geração do [Guid](../../Guid-Generation.md) para obter mais informações).
### Testando o BookAppService
Crie uma classe de teste denominada `BookAppService_Tests`no `Acme.BookStore.Application.Tests`projeto:
```csharp
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Xunit;
namespace Acme.BookStore
{
public class BookAppService_Tests : BookStoreApplicationTestBase
{
private readonly IBookAppService _bookAppService;
public BookAppService_Tests()
{
_bookAppService = GetRequiredService<IBookAppService>();
}
[Fact]
public async Task Should_Get_List_Of_Books()
{
//Act
var result = await _bookAppService.GetListAsync(
new PagedAndSortedResultRequestDto()
);
//Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(b => b.Name == "Test book 1");
}
}
}
```
- `Should_Get_List_Of_Books`O teste simplesmente usa o `BookAppService.GetListAsync`método para obter e verificar a lista de usuários.
Adicione um novo teste que crie um novo livro válido:
```csharp
[Fact]
public async Task Should_Create_A_Valid_Book()
{
//Act
var result = await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "New test book 42",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
//Assert
result.Id.ShouldNotBe(Guid.Empty);
result.Name.ShouldBe("New test book 42");
}
```
Adicione um novo teste que tente criar um livro inválido e falhe:
```csharp
[Fact]
public async Task Should_Not_Create_A_Book_Without_Name()
{
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _bookAppService.CreateAsync(
new CreateUpdateBookDto
{
Name = "",
Price = 10,
PublishDate = DateTime.Now,
Type = BookType.ScienceFiction
}
);
});
exception.ValidationErrors
.ShouldContain(err => err.MemberNames.Any(mem => mem == "Name"));
}
```
- Como o `Name`está vazio, o ABP lança um `AbpValidationException`.
Abra a **janela Test Explorer** (use o menu Test -> Windows -> Test Explorer, se não estiver visível) e **execute Todos os** testes:
![testes de serviço de livraria](images/bookstore-test-explorer.png)
Parabéns, ícones verdes mostram que os testes foram aprovados com sucesso!

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -0,0 +1,3 @@
## Unit of Work
Façam

@ -0,0 +1,3 @@
## Validation
Façam

@ -0,0 +1,3 @@
## Value Objects
Façam

@ -0,0 +1,328 @@
{
"items": [
{
"text": "Começando",
"items": [
{
"text": "De Modelo de Inicialização",
"items": [
{
"text": "Aplicativo com MVC (Razor Pages) UI",
"path": "Getting-Started-AspNetCore-MVC-Template.md"
},
{
"text": "Aplicativo com Angular UI",
"path": "Getting-Started-Angular-Template.md"
}
]
},
{
"text": "De Projetos Vazios",
"items": [
{
"text": "Com Aplicativo ASP.NET Core Web",
"path": "Getting-Started-AspNetCore-Application.md"
},
{
"text": "Com Aplicativo Console",
"path": "Getting-Started-Console-Application.md"
}
]
}
]
},
{
"text": "Tutoriais",
"items": [
{
"text": "Desenvolvimento de Aplicações",
"items": [
{
"text": "Com ASP.NET Core MVC UI",
"path": "Tutorials/AspNetCore-Mvc/Part-I.md"
},
{
"text": "Com Angular UI",
"path": "Tutorials/Angular/Part-I.md"
}
]
}
]
},
{
"text": "CLI",
"path": "CLI.md"
},
{
"text": "Fundamentos",
"items": [
{
"text": "Injeção de Dependência",
"path": "Dependency-Injection.md",
"items": [
{
"text": "Integração com Autofac",
"path": "Autofac-Integration.md"
}
]
},
{
"text": "Sistema de Arquivos Virtual",
"path": "Virtual-File-System.md"
},
{
"text": "Localização",
"path": "Localization.md"
},
{
"text": "Manipulação de Exceção",
"path": "Exception-Handling.md"
},
{
"text": "Validação"
},
{
"text": "Autorização"
},
{
"text": "Armazenamento em Cache"
},
{
"text": "Auditoria"
},
{
"text": "Gerenciamento de Configurações"
}
]
},
{
"text": "Eventos",
"items": [
{
"text": "Barramento de Eventos (local)"
},
{
"text": "Barramento de Eventos Distribuídos",
"items": [
{
"text": "Integração RabbitMQ"
}
]
}
]
},
{
"text": "Serviços",
"items": [
{
"text": "Serialização de Objetos"
},
{
"text": "Serialização JSON"
},
{
"text": "Emailing"
},
{
"text": "GUIDs"
},
{
"text": "Threading"
},
{
"text": "Timing"
}
]
},
{
"text": "Múltiplos Inquilinos",
"path": "Multi-Tenancy.md"
},
{
"text": "Desenvolvimento de Módulos",
"items": [
{
"text": "Fundamentos",
"path": "Module-Development-Basics.md"
},
{
"text": "Módulos de plug-in"
},
{
"text": "Melhores práticas",
"path": "Best-Practices/Index.md"
}
]
},
{
"text": "Design Orientado a Domínio",
"path": "Domain-Driven-Design.md",
"items": [
{
"text": "Camada de Domínio",
"items": [
{
"text": "Entidades & Raízes Agregadas",
"path": "Entities.md"
},
{
"text": "Value Objects"
},
{
"text": "Repositórios",
"path": "Repositories.md"
},
{
"text": "Serviços de Domínio"
},
{
"text": "Especificações"
}
]
},
{
"text": "Camada de Aplicação",
"items": [
{
"text": "Serviços de Aplicação",
"path": "Application-Services.md"
},
{
"text": "Objetos de Transferência de Dados"
},
{
"text": "Unidade de Trabalho"
}
]
}
]
},
{
"text": "ASP.NET Core",
"items": [
{
"text": "API",
"items": [
{
"text": "Controladores de API Automática",
"path": "AspNetCore/Auto-API-Controllers.md"
},
{
"text": "Clientes da API C # Dinâmica",
"path": "AspNetCore/Dynamic-CSharp-API-Clients.md"
}
]
},
{
"text": "Interface de Usuário",
"items": [
{
"text": "Gerenciamento de Pacotes do Lado do Cliente",
"path": "AspNetCore/Client-Side-Package-Management.md"
},
{
"text": "Compactação & Minificação",
"path": "AspNetCore/Bundling-Minification.md"
},
{
"text": "Tag Helpers",
"path": "AspNetCore/Tag-Helpers/Index.md"
},
{
"text": "Widgets",
"path": "AspNetCore/Widgets.md"
},
{
"text": "Theming",
"path": "AspNetCore/Theming.md"
}
]
}
]
},
{
"text": "Acesso de Dados",
"items": [
{
"text": "Integração do Entity Framework Core",
"path": "Entity-Framework-Core.md",
"items": [
{
"text": "Integração do PostgreSQL",
"path": "Best-Practices/PostgreSQL-Integration.md"
}
]
},
{
"text": "Integração do MongoDB",
"path": "MongoDB.md"
},
{
"text": "Integração do Dapper",
"path": "Dapper.md"
}
]
},
{
"text": "Background",
"items": [
{
"text": "Trabalhos em Segundo Plano",
"path": "Background-Jobs.md",
"items": [
{
"text": "Integração do Hangfire",
"path": "Background-Jobs-Hangfire.md"
},
{
"text": "Integração do RabbitMQ",
"path": "Background-Jobs-RabbitMq.md"
}
]
}
]
},
{
"text": "Modelos de Inicialização",
"path": "Startup-Templates/Index.md",
"items": [
{
"text": "Aplicativo",
"path": "Startup-Templates/Application.md"
},
{
"text": "Módulo",
"path": "Startup-Templates/Module.md"
}
]
},
{
"text": "Exemplos",
"items": [
{
"text": "Demonstração de Microsserviços",
"path": "Samples/Microservice-Demo.md"
}
]
},
{
"text": "Módulos de Aplicação",
"path": "Modules/Index.md"
},
{
"text": "Arquitetura de Microsserviços",
"path": "Microservice-Architecture.md"
},
{
"text": "Testando"
},
{
"text": "Compilações Noturnas",
"path": "Nightly-Builds.md"
},
{
"text": "Guia de Contribuição",
"path": "Contribution/Index.md"
}
]
}
Loading…
Cancel
Save