You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
abp/docs/pt-BR/Tutorials/AspNetCore-Mvc/Part-I.md

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:

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:

livraria-visual-studio-soluçã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.Domainconté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.Domainprojeto) da solução. A entidade principal do aplicativo é a Book. Crie uma classe, chamada Book, no Acme.BookStore.Domainprojeto, 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: AggregateRoote Entity. A raiz agregada é um dos conceitos de DDD (Domain Driven Design) . Consulte o documento da entidade para obter detalhes e melhores práticas.
  • Bookentidade herda AuditedAggregateRootque adiciona algumas propriedades de auditoria ( CreationTime, CreatorId, LastModificationTime... etc.) no topo da AggregateRootclasse.
  • Guidé o tipo de chave primária da Bookentidade.

BookType Enum

Defina a BookTypeenumeração no Acme.BookStore.Domain.Sharedprojeto:

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 DbSetpropriedade à BookStoreDbContextclasse no Acme.BookStore.EntityFrameworkCoreprojeto, conforme mostrado abaixo:

    public class BookStoreDbContext : AbpDbContext<BookStoreDbContext>
    {
        public DbSet<Book> Books { get; set; }
		...
    }

Configure sua entidade do livro

Abra o BookStoreDbContextModelCreatingExtensions.csarquivo no Acme.BookStore.EntityFrameworkCoreprojeto e adicione o seguinte código ao final do ConfigureBookStoremé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.DbMigrationscomo o projeto padrão e execute o seguinte comando:

livraria-pmc-add-book-migration

Isso criará uma nova classe de migração dentro da Migrationspasta. Em seguida, execute o Update-Databasecomando para atualizar o esquema do banco de dados:

PM> Update-Database

Adicionar dados de amostra

Update-DatabaseO comando criou a AppBookstabela no banco de dados. Abra seu banco de dados e insira algumas linhas de amostra, para que você possa mostrá-las na página:

livraria-livros-mesa

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 BookDtono Acme.BookStore.Application.Contractsprojeto:

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 do AuditedEntityDto<Guid>que possui propriedades de auditoria exatamente como a Bookclasse definida acima.

Será necessário converter Bookentidades em BookDtoobjetos 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 BookStoreApplicationAutoMapperProfileclasse no Acme.BookStore.Applicationprojeto:

using AutoMapper;

namespace Acme.BookStore
{
    public class BookStoreApplicationAutoMapperProfile : Profile
    {
        public BookStoreApplicationAutoMapperProfile()
        {
            CreateMap<Book, BookDto>();
        }
    }
}

CreateUpdateBookDto

Crie uma classe DTO denominada CreateUpdateBookDtono Acme.BookStore.Application.Contractsprojeto:

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 BookStoreApplicationAutoMapperProfiledo CreateUpdateBookDtoobjeto à Bookentidade:

CreateMap<CreateUpdateBookDto, Book>();

IBookAppService

Defina uma interface nomeada IBookAppServiceno Acme.BookStore.Application.Contractsprojeto:

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.
  • ICrudAppServicedefine comuns CRUD métodos: GetAsync, GetListAsync, CreateAsync, UpdateAsynce DeleteAsync. Não é necessário estendê-lo. Em vez disso, você pode herdar da IApplicationServiceinterface vazia e definir seus próprios métodos manualmente.
  • Existem algumas variações de ICrudAppServiceonde você pode usar DTOs separados para cada método.

BookAppService

Implemente IBookAppServicecomo nomeado BookAppServiceno Acme.BookStore.Applicationprojeto:

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.
  • BookAppServiceinjeta IRepository<Book, Guid>qual é o repositório padrão da Bookentidade. O ABP cria automaticamente repositórios padrão para cada raiz (ou entidade) agregada. Veja o documento do repositório .
  • BookAppServiceusa IObjectMapperpara converter Bookobjetos em BookDtoobjetos e CreateUpdateBookDtoobjetos em Bookobjetos. 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 Bookserviço e seus pontos de extremidade no estilo REST:

livraria-arrogância

O Swagger tem uma ótima interface para testar APIs. Você pode tentar executar a [GET] /api/app/bookAPI 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 $.ajaxou 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 do BookAppServiceconvertido em camelCase .
  • booké o nome convencional para o BookAppService(postfix do AppService removido e convertido em camelCase).
  • getListé o nome convencional para o GetListAsyncmétodo definido na AsyncCrudAppServiceclasse base (postfix assíncrono removido e convertido em camelCase).
  • {}O argumento é usado para enviar um objeto vazio ao GetListAsyncmétodo que normalmente espera um objeto do tipo PagedAndSortedResultRequestDtousado 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).
  • getListA função retorna a promise. Portanto, você pode passar um retorno de chamada para a função done(ou then) para obter o resultado do servidor.

A execução desse código produz a seguinte saída:

livraria-teste-js-proxy-getlist

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:

livraria-teste-js-proxy-getlist-rede

Vamos criar um novo livro usando a createfunçã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 Bookstabela no banco de dados para ver a nova linha do livro. Você pode tentar get, updatee deletefunciona 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 Bookspasta na Pagespasta do Acme.BookStore.Webprojeto e adicione uma nova página Razor denominada Index.cshtml:

livraria-add-index-page

Abra Index.cshtmle 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 o Acme.BookStore.Pages.Booksespaço para nome ou atualize-o no Index.cshtml.

Adicionar página de livros ao menu principal

Abra a BookStoreMenuContributorclasse na Menuspasta e adicione o seguinte código ao final do ConfigureMainMenuAsyncmé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/BookStorepasta do Acme.BookStore.Domain.Sharedprojeto:

arquivos de localização de livraria

Abra o en.jsonarquivo e adicione textos de localização Menu:BookStoree Menu:Bookschaves 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:

itens-menu-livraria

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.cshtmlseguinte:

@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 a scripttag 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-carde abp-tablesã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.jsarquivo JavaScript na Pages/Books/pasta:

arquivo-index-js-bookstore

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:

livraria-lista-de-livros

Próxima parte

Veja a próxima parte deste tutorial.