Exercício – Configurar uma migração
Nesta unidade, vai criar classes de entidade C# que serão mapeadas para tabelas numa base de dados SQLite local. A funcionalidade migrações do EF Core produz tabelas dessas entidades.
Uma migração fornece uma forma de atualizar incrementalmente o esquema da base de dados.
Obter os ficheiros do projeto
Para começar, obtenha os ficheiros do projeto. Tem algumas opções para obter os ficheiros do projeto:
- Utilizar o GitHub Codespaces
- Clonar o repositório do GitHub
Se tiver um runtime de contentor compatível instalado, também pode utilizar a extensão Dev Containers para abrir o repositório num contentor com as ferramentas pré-instaladas.
Utilizar o GitHub Codespaces
Um espaço de código é um IDE alojado na cloud. Se estiver a utilizar o GitHub Codespaces, aceda ao repositório no seu browser. Selecione Código e, em seguida, crie um novo espaço de código no main
ramo .
Clonar o repositório do GitHub
Se não estiver a utilizar o GitHub Codespaces, pode clonar o repositório do GitHub do projeto e, em seguida, abrir os ficheiros como uma pasta no Visual Studio Code.
Abra um terminal de comandos e, em seguida, clone o projeto a partir do GitHub com a linha de comandos:
git clone https://github.com/MicrosoftDocs/mslearn-persist-data-ef-core
Aceda à pasta mslearn-persist-data-ef-core e, em seguida, abra o projeto no Visual Studio Code:
cd mslearn-persist-data-ef-core code .
Rever o código
Agora tem os ficheiros do projeto com os quais trabalhar. Veja o que está no projeto e reveja o código.
- O projeto, uma API Web ASP.NET Core, está localizado no diretório ContosoPizza. Os caminhos de ficheiro referidos neste módulo são relativos ao diretório ContosoPizza .
- Services/PizzaService.cs é uma classe de serviço que define métodos CRUD (criar, ler, atualizar e eliminar). Todos os métodos atualmente lançam
System.NotImplementedException
. - Em Program.cs,
PizzaService
está registado no sistema de injeção de dependências ASP.NET Core. - Controllers/PizzaController.cs é um valor que
ApiController
expõe um ponto final para verbos HTTP POST, GET, PUT e DELETE. Estes verbos chamam os métodos CRUD correspondentes emPizzaService
.PizzaService
é injetado noPizzaController
construtor. - A pasta Modelos contém os modelos que são utilizados por
PizzaService
ePizzaController
. - Os modelos de entidades , Pizza.cs, Topping.cs e Sauce.cs, têm as seguintes relações:
- Uma pizza pode ter uma ou mais coberturas.
- Uma cobertura pode ser usada em uma ou em muitas pizzas.
- Uma pizza pode ter um molho, mas um molho pode ser usado em muitas pizzas.
Criar a aplicação
Para criar a aplicação no Visual Studio Code:
No painel Explorador , clique com o botão direito do rato no diretório ContosoPizza e selecione Abrir no Terminal Integrado.
É aberto um painel de terminal no âmbito do diretório ContosoPizza .
Crie a aplicação com o seguinte comando:
dotnet build
O código deve ser compilar sem avisos ou erros.
Adicionar pacotes NuGet e ferramentas EF Core
O motor de base de dados com o qual trabalha neste módulo é o SQLite. O SQLite é um motor de base de dados leve e baseado em ficheiros. É uma boa opção para desenvolvimento e teste e também é uma boa opção para implementações de produção em pequena escala.
Nota
Conforme mencionado anteriormente, os fornecedores de bases de dados no EF Core são plug-able. O SQLite é uma boa opção para este módulo, uma vez que é simples e multiplataforma. Pode utilizar o mesmo código para trabalhar com diferentes motores de base de dados, como SQL Server e PostgreSQL. Pode até utilizar vários motores de base de dados na mesma aplicação.
Antes de começar, adicione os pacotes necessários:
No painel de terminal, execute o seguinte comando:
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
Este comando adiciona o pacote NuGet que contém o fornecedor de base de dados SQLite EF Core e todas as respetivas dependências, incluindo os serviços comuns do EF Core.
Execute este comando:
dotnet add package Microsoft.EntityFrameworkCore.Design
Este comando adiciona pacotes necessários para as ferramentas do EF Core.
Para concluir, execute este comando:
dotnet tool install --global dotnet-ef
Este comando instala
dotnet ef
, a ferramenta que irá utilizar para criar migrações e andaimes.Dica
Se
dotnet ef
já estiver instalado, pode atualizá-lo ao executardotnet tool update --global dotnet-ef
.
Estruturar modelos e DbContext
Agora, vai adicionar e configurar uma DbContext
implementação. DbContext
é um gateway através do qual pode interagir com a base de dados.
Clique com o botão direito do rato no diretório ContosoPizza e adicione uma nova pasta denominada Dados.
Na pasta Dados , crie um novo ficheiro com o nome PizzaContext.cs. Adicione o seguinte código ao ficheiro vazio:
using Microsoft.EntityFrameworkCore; using ContosoPizza.Models; namespace ContosoPizza.Data; public class PizzaContext : DbContext { public PizzaContext (DbContextOptions<PizzaContext> options) : base(options) { } public DbSet<Pizza> Pizzas => Set<Pizza>(); public DbSet<Topping> Toppings => Set<Topping>(); public DbSet<Sauce> Sauces => Set<Sauce>(); }
No código anterior:
- O construtor aceita um parâmetro do tipo
DbContextOptions<PizzaContext>
. O construtor permite que o código externo passe na configuração para que o mesmoDbContext
possa ser partilhado entre código de teste e de produção e até mesmo ser utilizado com diferentes fornecedores. - As
DbSet<T>
propriedades correspondem às tabelas a criar na base de dados. - Os nomes das tabelas corresponderão aos nomes das propriedades
DbSet<T>
na classePizzaContext
. Se necessário, pode substituir este comportamento. - Quando instanciado,
PizzaContext
expõePizzas
as propriedades ,Toppings
eSauces
. As alterações efetuadas às coleções expostas por essas propriedades serão propagadas para a base de dados.
- O construtor aceita um parâmetro do tipo
Em Program.cs, substitua
// Add the PizzaContext
pelo seguinte código:builder.Services.AddSqlite<PizzaContext>("Data Source=ContosoPizza.db");
O código anterior:
- Regista-se no sistema de injeção de dependências
PizzaContext
ASP.NET Core. - Especifica que
PizzaContext
irá utilizar o fornecedor de base de dados SQLite. - Define uma cadeia de ligação SQLite que aponta para um ficheiro local, ContosoPizza.db.
Nota
O SQLite utiliza ficheiros de base de dados locais, pelo que provavelmente não há problema em codificar a cadeia de ligação. Para bases de dados de rede como PostgreSQL e SQL Server, deve sempre armazenar as cadeias de ligação de forma segura. Para desenvolvimento local, utilize o Gestor de Segredos. Para implementações de produção, considere utilizar um serviço como o Azure Key Vault.
- Regista-se no sistema de injeção de dependências
Também em Program.cs, substitua
// Additional using declarations
pelo seguinte código.using ContosoPizza.Data;
Este código resolve as dependências no passo anterior.
Guarde todas as alterações. O GitHub Codespaces guarda as suas alterações automaticamente.
Crie a aplicação no terminal ao executar
dotnet build
. A compilação deve ser bem-sucedida sem avisos ou erros.
Criar e executar uma migração
Em seguida, crie uma migração que pode utilizar para criar a sua base de dados inicial.
Execute o seguinte comando para gerar uma migração para criar as tabelas de base de dados:
dotnet ef migrations add InitialCreate --context PizzaContext
No comando anterior:
- É dado o nome InitialCreate à migração.
- A opção
--context
especifica o nome da classe no projeto ContosoPizza, que deriva deDbContext
.
É apresentado um novo diretório Migrations na raiz do projeto ContosoPizza. O diretório contém um <timestamp>_InitialCreate.cs ficheiro que descreve as alterações da base de dados a traduzir para um script de alteração da Linguagem de Definição de Dados (DDL).
Execute o seguinte comando para aplicar a migração InitialCreate:
dotnet ef database update --context PizzaContext
Este comando aplica a migração. ContosoPizza.db não existe, pelo que a migração é criada no diretório do projeto.
Dica
A ferramenta
dotnet ef
é suportada em todas as plataformas. No Visual Studio no Windows, pode utilizar os cmdlets eUpdate-Database
doAdd-Migration
PowerShell na janela da Consola integrada do Gestor de Pacotes.
Inspecionar a base de dados
O EF Core criou uma base de dados para a sua aplicação. Em seguida, veja dentro da base de dados com a extensão SQLite.
No painel Explorador , clique com o botão direito do rato no ficheiro ContosoPizza.db e selecione Abrir Base de Dados.
É apresentada uma pasta do Explorador do SQLite no painel Explorador .
Selecione a pasta EXPLORADOR DO SQLite para expandir o nó e todos os nós subordinados. Clique com o botão direito do rato em ContosoPizza.db e selecione Mostrar Tabela 'sqlite_master' para ver o esquema completo da base de dados e as restrições que a migração criou.
- Foram criadas tabelas que correspondem a cada entidade.
- Os nomes das tabelas foram retirados dos nomes das
DbSet
propriedades noPizzaContext
. - As propriedades nomeadas
Id
foram inferidas como estando a criar automaticamente campos de chave primária. - As convenções de nomenclatura de chave primária e de restrição de chave externa do EF Core são
PK_<primary key property>
eFK_<dependent entity>_<principal entity>_<foreign key property>
, respetivamente. Os marcadores de posição<dependent entity>
e<principal entity>
correspondem aos nomes das classes de entidades.
Nota
Tal como ASP.NET Core MVC, o EF Core utiliza uma convenção sobre a abordagem de configuração. As convenções do EF Core reduzem o tempo de desenvolvimento ao inferirem a intenção do programador. Por exemplo, uma propriedade com o nome
Id
ou<entity name>Id
é inferida para ser a chave primária da tabela gerada. Se optar por não adotar a convenção de nomenclatura, a propriedade tem de ser anotada com o[Key]
atributo ou configurada como uma chave noOnModelCreating
método doDbContext
.
Alterar o modelo e atualizar o esquema da base de dados
O seu gestor na Contoso Pizza fornece-lhe alguns requisitos novos, pelo que tem de alterar os modelos de entidade. Nos passos seguintes, pode modificar os modelos com atributos de mapeamento (por vezes denominados anotações de dados).
Em Models\Pizza.cs, faça as seguintes alterações:
- Adicione uma
using
diretiva paraSystem.ComponentModel.DataAnnotations
. - Adicione um
[Required]
atributo antes daName
propriedade para marcar a propriedade como necessária. - Adicione um
[MaxLength(100)]
atributo antes daName
propriedade para especificar um comprimento de cadeia máximo de 100.
using System.ComponentModel.DataAnnotations; namespace ContosoPizza.Models; public class Pizza { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public Sauce? Sauce { get; set; } public ICollection<Topping>? Toppings { get; set; } }
- Adicione uma
Em Models\Sauce.cs, faça as seguintes alterações:
- Adicione uma
using
diretiva paraSystem.ComponentModel.DataAnnotations
. - Adicione um
[Required]
atributo antes daName
propriedade para marcar a propriedade como necessária. - Adicione um
[MaxLength(100)]
atributo antes daName
propriedade para especificar um comprimento de cadeia máximo de 100. - Adicione uma
bool
propriedade com o nomeIsVegan
.
using System.ComponentModel.DataAnnotations; namespace ContosoPizza.Models; public class Sauce { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public bool IsVegan { get; set; } }
- Adicione uma
Em Models\Topping.cs, faça as seguintes alterações:
- Adicione
using
diretivas paraSystem.ComponentModel.DataAnnotations
eSystem.Text.Json.Serialization
. - Adicione um
[Required]
atributo antes daName
propriedade para marcar a propriedade como necessária. - Adicione um
[MaxLength(100)]
atributo antes daName
propriedade para especificar um comprimento de cadeia máximo de 100. - Adicione uma
decimal
propriedade com o nomeCalories
imediatamente após aName
propriedade . - Adicione uma
Pizzas
propriedade do tipoICollection<Pizza>?
para criarPizza
-Topping
uma relação muitos-para-muitos. - Adicione um
[JsonIgnore]
atributo àPizzas
propriedade .
Importante
Estes passos impedem
Topping
que as entidades incluíssem aPizzas
propriedade quando o código da API Web serializa a resposta ao JSON. Sem esta alteração, uma coleção serializada de coberturas incluiria uma coleção de cada pizza que usa a cobertura. Cada pizza nessa coleção conteria uma coleção de coberturas, que cada uma novamente conteria uma coleção de pizzas. Este tipo de ciclo infinito é denominado referência circular e não pode ser serializado.using System.ComponentModel.DataAnnotations; using System.Text.Json.Serialization; namespace ContosoPizza.Models; public class Topping { public int Id { get; set; } [Required] [MaxLength(100)] public string? Name { get; set; } public decimal Calories { get; set; } [JsonIgnore] public ICollection<Pizza>? Pizzas { get; set; } }
- Adicione
Guarde todas as alterações e execute
dotnet build
.Execute o seguinte comando para gerar uma migração para criar as tabelas de base de dados:
dotnet ef migrations add ModelRevisions --context PizzaContext
É criada uma migração com o nome ModelRevisions .
Nota
Esta mensagem é apresentada: foi estruturada uma operação que pode resultar na perda de dados. Reveja a migração para obter precisão. A mensagem é apresentada porque alterou a relação de
Pizza
umTopping
para muitos para muitos, o que requer que uma coluna de chave externa existente seja removida. Uma vez que ainda não tem dados na base de dados, esta alteração não é problemática. No entanto, em geral, é aconselhável verificar a migração gerada quando este aviso aparecer para garantir que não existem dados eliminados ou truncados pela migração.Execute o seguinte comando para aplicar a migração ModelRevisions:
dotnet ef database update --context PizzaContext
Na barra de título da pasta SQLite Explorer , selecione o botão Atualizar Bases de Dados .
Na pasta EXPLORADOR DO SQLite , clique com o botão direito do rato em ContosoPizza.db. Selecione Mostrar Tabela "sqlite_master" para ver o esquema e as restrições completos da base de dados.
Importante
A extensão SQLite reutiliza os separadores SQLite abertos.
- Foi criada uma
PizzaTopping
tabela de associação para representar a relação muitos-para-muitos entre pizzas e coberturas. - Foram adicionados novos campos a
Toppings
eSauces
.Calories
é definido como umatext
coluna porque o SQLite não tem um tipo correspondentedecimal
.- Da mesma forma,
IsVegan
é definido como umainteger
coluna. O SQLite não define umbool
tipo. - Em ambos os casos, o EF Core gere a tradução.
- A
Name
coluna em cada tabela foi marcadanot null
, mas o SQLite não tem umaMaxLength
restrição.
Dica
Os fornecedores de bases de dados EF Core mapeiam um esquema de modelo para as funcionalidades de uma base de dados específica. Embora o SQLite não implemente uma restrição correspondente para
MaxLength
o , outras bases de dados, como SQL Server e PostgreSQL, sim.- Foi criada uma
Na pasta EXPLORADOR DO SQLite , clique com o botão direito do rato na
_EFMigrationsHistory
tabela e selecione Mostrar Tabela. A tabela contém uma lista de todas as migrações que são aplicadas à base de dados. Uma vez que executou duas migrações, existem duas entradas: uma para a migração InitialCreate e outra para ModelRevisions.
Nota
Este exercício utilizou atributos de mapeamento (anotações de dados) para mapear modelos para a base de dados. Como alternativa aos atributos de mapeamento, pode utilizar a API Fluent do ModelBuilder para configurar modelos. Ambas as abordagens são válidas, mas alguns programadores preferem uma abordagem em vez da outra.
Utilizou migrações para definir e atualizar um esquema de base de dados. Na próxima unidade, irá concluir os métodos nessa PizzaService
manipulação de dados.