22 KiB
Tutorial do ASP.NET Core MVC - Parte I
Sobre este tutorial
Nesta série de tutoriais, você criará um aplicativo usado para gerenciar uma lista de livros e seus autores. O Entity Framework Core (EF Core) será usado como o provedor ORM, pois é o provedor de banco de dados padrão.
Esta é a primeira parte da série de tutoriais do ASP.NET Core MVC. 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
- Parte III: Testes de Integração
Você pode acessar o código fonte do aplicativo no repositório GitHub .
Você também pode assistir a este curso em vídeo preparado por um membro da comunidade ABP, com base neste tutorial.
Criando o projeto
Crie um novo projeto chamado Acme.BookStore
, crie o banco de dados e execute o aplicativo seguindo o documento Introdução.
Estrutura da solução
É assim que a estrutura da solução em camadas cuida da criação:
Você pode ver o documento do modelo de aplicativo 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 , serviços de domínio 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 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:
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
eEntity
. A raiz agregada é um dos conceitos de DDD (Domain Driven Design) . Consulte o documento da entidade para obter detalhes e melhores práticas. Book
entidade herdaAuditedAggregateRoot
que adiciona algumas propriedades de auditoria (CreationTime
,CreatorId
,LastModificationTime
... etc.) no topo daAggregateRoot
classe.Guid
é o tipo de chave primária daBook
entidade.
BookType Enum
Defina a BookType
enumeração no Acme.BookStore.Domain.Shared
projeto:
namespace Acme.BookStore
{
public enum BookType
{
Undefined,
Adventure,
Biography,
Dystopia,
Fantastic,
Horror,
Science,
ScienceFiction,
Poetry
}
}
Adicionar entidade de livro ao seu DbContext
O EF Core exige que você relacione entidades com seu DbContext. A maneira mais fácil de fazer isso é adicionar uma DbSet
propriedade à BookStoreDbContext
classe no Acme.BookStore.EntityFrameworkCore
projeto, conforme mostrado abaixo:
public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
{
public DbSet<Book> Books { get; set; }
...
}
Configure sua entidade do livro
Abra o BookStoreDbContextModelCreatingExtensions.cs
arquivo no Acme.BookStore.EntityFrameworkCore
projeto e adicione o seguinte código ao final do ConfigureBookStore
método para configurar a entidade Livro:
builder.Entity<Book>(b =>
{
b.ToTable(BookStoreConsts.DbTablePrefix + "Books", BookStoreConsts.DbSchema);
b.ConfigureByConvention(); //auto configure for the base class props
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
Adicionar nova migração e atualizar o banco de dados
O modelo de inicialização usa as primeiras migrações do código principal EF para criar e manter o esquema do banco de dados. Abra o Gerenciador de Console Package (PMC) (sob as Ferramentas / Gerente Nuget Package menu), selecione o Acme.BookStore.EntityFrameworkCore.DbMigrations
como o projeto padrão e execute o seguinte comando:
Isso criará uma nova classe de migração dentro da Migrations
pasta. Em seguida, execute o Update-Database
comando para atualizar o esquema do banco de dados:
PM> Update-Database
Adicionar dados de amostra
Update-Database
O comando criou a AppBooks
tabela no banco de dados. Abra seu banco de dados e insira algumas linhas de amostra, para que você possa mostrá-las na página:
Crie o serviço de aplicativo
O próximo passo é criar um serviço de aplicativo 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:
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 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 doAuditedEntityDto<Guid>
que possui propriedades de auditoria exatamente como aBook
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 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:
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:
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 pela estrutura ABP.
Em seguida, adicione um mapeamento BookStoreApplicationAutoMapperProfile
do CreateUpdateBookDto
objeto à Book
entidade:
CreateMap<CreateUpdateBookDto, Book>();
IBookAppService
Defina uma interface nomeada IBookAppService
no Acme.BookStore.Application.Contracts
projeto:
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
eDeleteAsync
. Não é necessário estendê-lo. Em vez disso, você pode herdar daIApplicationService
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:
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 doCrudAppService<...>
qual implementa todos os métodos CRUD definidos acima.BookAppService
injetaIRepository<Book, Guid>
qual é o repositório padrão daBook
entidade. O ABP cria automaticamente repositórios padrão para cada raiz (ou entidade) agregada. Veja o documento do repositório .BookAppService
usaIObjectMapper
para converterBook
objetos emBookDto
objetos eCreateUpdateBookDto
objetos emBook
objetos. O modelo de inicialização usa a biblioteca AutoMapper 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 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 usando a biblioteca Swashbuckle.AspNetCore . Execute o 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:
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.
Proxies dinâmicos de JavaScript
É comum chamar pontos de extremidade da API HTTP via AJAX do lado do JavaScript . Você pode usar $.ajax
ou outra ferramenta para chamar os pontos de extremidade. No entanto, o ABP oferece uma maneira melhor.
O ABP cria dinamicamente proxies JavaScript para todos os pontos de extremidade da API. Portanto, você pode usar qualquer terminal, assim como chamar uma função JavaScript .
Testando no console do desenvolvedor do navegador
Você pode testar facilmente os proxies JavaScript usando o Console do desenvolvedor do seu navegador favorito agora. Execute o aplicativo, abra as ferramentas de desenvolvedor do navegador (atalho: F12), vá para a guia Console , digite o seguinte código e pressione enter:
acme.bookStore.book.getList({}).done(function (result) { console.log(result); });
acme.bookStore
é o espaço para nome doBookAppService
convertido em camelCase .book
é o nome convencional para oBookAppService
(postfix do AppService removido e convertido em camelCase).getList
é o nome convencional para oGetListAsync
método definido naAsyncCrudAppService
classe base (postfix assíncrono removido e convertido em camelCase).{}
O argumento é usado para enviar um objeto vazio aoGetListAsync
método que normalmente espera um objeto do tipoPagedAndSortedResultRequestDto
usado para enviar opções de paginação e classificação ao servidor (todas as propriedades são opcionais, para que você possa enviar um objeto vazio).getList
A função retorna apromise
. Portanto, você pode passar um retorno de chamada para a funçãodone
(outhen
) para obter o resultado do servidor.
A execução desse código produz a seguinte saída:
Você pode ver a lista de livros retornada do servidor. Você também pode verificar a guia de rede das ferramentas do desenvolvedor para ver a comunicação do cliente com o servidor:
Vamos criar um novo livro usando a create
função:
acme.bookStore.book.create({ name: 'Foundation', type: 7, publishDate: '1951-05-24', price: 21.5 }).done(function (result) { console.log('successfully created the book with id: ' + result.id); });
Você deve ver uma mensagem no console, algo assim:
successfully created the book with id: f3f03580-c1aa-d6a9-072d-39e75c69f5c7
Verifique a Books
tabela no banco de dados para ver a nova linha do livro. Você pode tentar get
, update
e delete
funciona mesmo.
Crie a página de livros
É hora de criar algo visível e utilizável! Em vez do MVC clássico, usaremos a nova abordagem de interface do usuário do Razor Pages, recomendada pela Microsoft.
Crie uma nova Books
pasta na Pages
pasta do Acme.BookStore.Web
projeto e adicione uma nova página Razor denominada Index.cshtml
:
Abra Index.cshtml
e altere o conteúdo, como mostrado abaixo:
@page
@using Acme.BookStore.Web.Pages.Books
@model IndexModel
<h2>Books</h2>
- Verifique se o
IndexModel
( Index.cshtml.cs) possui oAcme.BookStore.Pages.Books
espaço para nome ou atualize-o noIndex.cshtml
.
Adicionar página de livros ao menu principal
Abra a BookStoreMenuContributor
classe na Menus
pasta e adicione o seguinte código ao final do ConfigureMainMenuAsync
método:
context.Menu.AddItem(
new ApplicationMenuItem("BooksStore", l["Menu:BookStore"])
.AddItem(new ApplicationMenuItem("BooksStore.Books", l["Menu:Books"], url: "/Books"))
);
Localizando os itens de menu
Os textos de localização estão localizados na Localization/BookStore
pasta do Acme.BookStore.Domain.Shared
projeto:
Abra o en.json
arquivo e adicione textos de localização Menu:BookStore
e Menu:Books
chaves ao final do arquivo:
{
"culture": "en",
"texts": {
"Menu:BookStore": "Book Store",
"Menu:Books": "Books"
}
}
- O sistema de localização da ABP é construído no sistema de localização padrão do ASP.NET Core e o estende de várias maneiras. Consulte o documento de localização para obter detalhes.
- Os nomes das chaves de localização são arbitrários. Você pode definir qualquer nome. Preferimos adicionar
Menu:
prefixo aos itens de menu para distinguir de outros textos. Se um texto não estiver definido no arquivo de localização, ele recuará para a chave de localização (comportamento padrão do ASP.NET Core).
Execute o aplicativo e veja se o novo item de menu foi adicionado à barra superior:
Quando você clica no item de menu Livros, você é redirecionado para a nova página Livros.
Lista de livros
Usaremos o plug-in Datatables.net JQuery para mostrar a lista de tabelas na página. As tabelas de dados podem funcionar completamente via AJAX, são rápidas e oferecem uma boa experiência ao usuário. O plug-in Datatables está configurado no modelo de inicialização, para que você possa usá-lo diretamente em qualquer página sem incluir nenhum estilo ou arquivo de script em sua página.
Index.cshtml
Altere o Pages/Books/Index.cshtml
seguinte:
@page
@model Acme.BookStore.Web.Pages.Books.IndexModel
@section scripts
{
<abp-script src="/Pages/Books/index.js" />
}
<abp-card>
<abp-card-header>
<h2>@L["Books"]</h2>
</abp-card-header>
<abp-card-body>
<abp-table striped-rows="true" id="BooksTable">
<thead>
<tr>
<th>@L["Name"]</th>
<th>@L["Type"]</th>
<th>@L["PublishDate"]</th>
<th>@L["Price"]</th>
<th>@L["CreationTime"]</th>
</tr>
</thead>
</abp-table>
</abp-card-body>
</abp-card>
abp-script
O auxiliar de marca é usado para adicionar scripts externos à página. Possui muitos recursos adicionais em comparação com ascript
tag padrão . Ele lida com minificação e controle de versão, por exemplo. Consulte o documento de compactação e redução para obter detalhes.abp-card
eabp-table
são auxiliares de tags para o componente de cartão do Twitter Bootstrap . Existem muitos auxiliares de tag no ABP para usar facilmente a maioria dos componentes de autoinicialização . Você também pode usar tags HTML regulares em vez desses auxiliares de tag, mas o uso de tag reduz o código HTML e evita erros com a ajuda do intellisense e da verificação do tipo de tempo de compilação. Consulte o documento auxiliares da tag .- Você pode localizar os nomes das colunas no arquivo de localização, como fez nos itens de menu acima.
Adicionar um arquivo de script
Crie um index.js
arquivo JavaScript na Pages/Books/
pasta:
index.js
o conteúdo é mostrado abaixo:
$(function () {
var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({
ajax: abp.libs.datatables.createAjax(acme.bookStore.book.getList),
columnDefs: [
{ data: "name" },
{ data: "type" },
{ data: "publishDate" },
{ data: "price" },
{ data: "creationTime" }
]
}));
});
abp.libs.datatables.createAjax
é uma função auxiliar para adaptar os proxies dinâmicos da API JavaScript da ABP ao formato do Datatable.abp.libs.datatables.normalizeConfiguration
é outra função auxiliar. Não há necessidade de usá-lo, mas simplifica a configuração das tabelas de dados, fornecendo valores convencionais para as opções ausentes.acme.bookStore.book.getList
é a função para obter a lista de livros (você já viu isso antes).- Consulte a documentação do Datatable para obter mais opções de configuração.
A interface do usuário final é mostrada abaixo:
Próxima parte
Veja a próxima parte deste tutorial.