EF Core - Aplicando o Migrations
Neste artigo veremos maneiras diferentes de aplicar o Migrations usando o EF Core. |
Se você decidiu usar o Entity Framework (abordagem Code-First) em seu próximo projeto, mais cedo ou mais tarde, você terá que fazer alterações em seu banco de dados por meio de migrações.
Neste artigo, consideraremos como trabalhar com migrações do EF em geral e como executá-las como parte de seu pipeline de DevOps.
Vou usar como projeto exemplo uma Web API criada no ambiente do .NET 7.0.100-rc.2.22477.23.
A seguir vou mostrar de forma resumida as etapas para criar a solução e o projeto usando a ferramenta CLI e o PowerShell:
1- Crie uma pasta onde deseja criar a solução. Eu criei a pasta efcoremigrationsapp
2- Crie uma solução EFMigrations usando o comando : dotnet new sln --name EFMigrations
3- Crie um projeto web ASP.NET Core Api vazio : dotnet new web --name Api
4- A seguir inclua o projeto Api na solução EFMigrations: dotnet sln add .\Api\Api.csproj
Abaixo temos a figura com a sequência de comandos usada:
5- Entre na pasta do projeto Api e inclua os seguintes pacotes no projeto:
dotnet add package Swashbuckle.AspNetCore
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
Verificando o arquivo Api.csproj temos os pacotes instalados :
<ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.10" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" /> </ItemGroup> |
Assim vamos usar o provedor do SQLite de forma a ter um database leve e rápido.
A seguir vamos definir a configuração para fazer com que nossa aplicãção inicie usando o Swagger por padrão; Abra o arquivo launchSettings.json e adicione a seguinte linha aos perfis Api e IIS Express:
"launchUrl": "swagger"
A seguir inclua no arquivo Program.cs o código abaixo:
var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build(); app.UseSwagger();
app.UseSwaggerUI();
app.Run(); |
Com isso concluímos a configuração padrão inicial básica. Vamos agora criar o arquivo de contexto e as entidades.
Aqui você pode abrir o projeto usando o VS Code ou o Visual Studio 2022 preview (pois estamos usando o .NET 7.0 que não foi lançado oficialmente ainda)
Criando a modelo e o contexto
Crie no projeto uma pasta Models e nesta pasta crie a classe Usuario :
public class Usuario
{
public int Id { get; set; }
public string Nome { get; set; } = string.Empty;
}
|
Crie outra pasta no projeto chamada Context e a seguir crie a classe AppDbContext que herda de DbContext :
using Api.Models;
using Microsoft.EntityFrameworkCore;
namespace Api.Context; public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions options) : base(options) { }
public DbSet<Usuario> Usuarios => Set<Usuario>();
}
|
A seguir podemos registrar nosso contexto no container DI da ASP.NET Core; para fazer isso, precisamos modificar Program.cs da seguinte maneira:
using Api.Context;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
string connectionSqlite = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlite(connectionSqlite));
var app = builder.Build(); app.UseSwagger();
app.UseSwaggerUI();
app.MapGet("/usuarios", (CancellationToken cancellationToken) =>
{
using var scope = app.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
return dbContext.Usuarios.ToListAsync(cancellationToken);
});
app.Run(); |
Configuramos o
contexto para se conectar ao banco de dados SQLite
e adicionamos um endpoint simples para obter todos os usuários do nosso banco de
dados.
Como você pode ver, recuperamos a string de conexão do banco de dados da classe
Configuration, então para fazer isso funcionar,
também precisamos atualizar o appsettings.json da
seguinte maneira :
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=SQLite.db"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
|
Agora a configuração esta pronta e podemos iniciar aplicando o Migrations.
Instalando a ferramenta dotnet ef tools
As ferramentas de interface de linha de comando (CLI) para o Entity Framework Core executam tarefas de desenvolvimento em tempo de projeto.
Por exemplo, elas criam migrações, aplicam migrações e geram código para um modelo baseado em um banco de dados existente. Os comandos são uma extensão do comando dotnet , que faz parte do SDK do .NET Core. Essas ferramentas funcionam com projetos .NET Core.
Para verificar se a ferramenta esta instalada no seu ambiente emita o comando: dotnet ef
Você deverá visualiar a imagem acima com informações da versão.
Para instalar a ferramenta que aplica a migração podemos usar o comando :
dotnet tool install --global dotnet-ef
Para atualizar a ferramenta no seu ambiente o comando é :
dotnet tool update --global dotnet-ef
Aplicando a migração e criando o banco de dados
Para aplicar uma migração inicial e criar o arquivo de script e a pasta Migrations no projeto posicione-se na pasta do projeto e emita o comando :
dotnet ef migrations add Inicial
Ou, se tiver uma solução com mais de um projeto, posicione-se na pasta da solução e emita o comando:
dotnet ef migrations add Inicial --project NomeProjeto -s ProjetoAPi -c ArquivoContexto --verbose
Para o nosso exemplo o comando seria:
dotnet ef migrations add Inicial --project Api -s Api -c AppDbContext --verbose
Com isso será criada a pasta Migrations no projeto contendo o arquivo de script com este conteúdo :
public partial class Inicial :
Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "Usuarios", columns: table => new { Id = table.Column<int>(type: "INTEGER", nullable: false) .Annotation("Sqlite:Autoincrement", true), Nome = table.Column<string>(type: "TEXT", nullable: false) }, constraints: table => { table.PrimaryKey("PK_Usuarios", x => x.Id); }); }
protected override void Down(MigrationBuilder
migrationBuilder) |
Temos no arquivo de scripts , no método Up, os comandos que serão executados para criar a tabela Usuarios com as colunas Id e Nome , a chave primária e, no método Down, o comando para desfazer a operação.
Para aplicar estes comandos basta executar o seguinte comando:
dotnet ef database update
Ou, se tiver uma solução com mais de um projeto:
dotnet ef database update Inicial --project Api -s Api -c AppDbContext --verbose
Com isso teremos o banco de dados SQLite.db criado na raiz do projeto:
Se você quiser usar uma ferramenta para gerenciar e administrar o banco de dados SQLite pode usar o DB Browser for SQLite.
Aplicando migrações em CI/CD
Geralmente, há três maneiras de lidar com as migrações do EF no pipeline do
DevOps:
1. Usar Scripts SQL
O Entity Framework Core nos permite gerar scripts SQL puros com base nas
migrações. Tudo o que precisamos fazer é executar o seguinte comando:
dotnet ef migrations script
Para o nosso exemplo, posicionando-se na pasta Api e emitindo o comando acima iremos obter:
Com isso em mente, podemos criar scripts durante o pipeline Build, publicá-los como artefatos e executá-los durante o pipeline Release.
2. Gerando pacotes de migração do EF Core compatíveis
com DevOps
Com o .NET 6, a equipe do EF Core lançou um novo recurso chamado mibrations
bundles ou pacotes de migração. A ideia é que você possa gerar um arquivo
executável contendo tudo o que é necessário para executar as migrações. Podemos
criar um pacote executando o seguinte comando:
dotnet ef migrations bundle --configuration Bundle
Como resultado, obteremos o arquivo efbundle.exe
que pode ser executado para aplicar as migrações ao banco de dados. Embora essa
abordagem possa ser preferível em alguns casos, o algoritmo geral permanece o
mesmo dos scripts SQL. Ainda precisamos produzir um arquivo como parte do
processo de CI e executá-lo como parte do CD.
3. Aplicando o Mibrations na inicialização do
aplicativo
O Entity Framework nos fornece uma opção para executar migrações vis código
executando o método Database.Migrate().
Considerando isso, podemos simplesmente colocar esse método no início do Program.cs e executar as migrações durante a inicialização da aplicação. O código pode ter a seguinte aparência:
using Microsoft.EntityFrameworkCore;
using WebApi.DataAccess;
var builder = WebApplication.CreateBuilder(args); string connectionSqlite = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlite(connectionSqlite));
var app = builder.Build();
using var scope = app.Services.CreateScope();
await using var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await dbContext.Database.MigrateAsync();
app.Run();
|
Embora essa seja a maneira mais rápida e fácil de lidar com migrações, a abordagem é arriscada e não é recomendada para aplicativos de produção.
Se vários nós de aplicativos forem executados simultaneamente, eles poderão tentar aplicar migrações e atualizar o banco de dados simultaneamente, o que causará falhas ou corrupção de dados.
E estamos conversados...
Pegue o projeto aqui : EFCoreMigrationsApp.zip
"Bendito seja o Deus e Pai de nosso Senhor Jesus
Cristo que, segundo a sua grande misericórdia, nos gerou de novo para uma viva
esperança, pela ressurreição de Jesus Cristo dentre os mortos"
1 Pedro 1:3
Referências: