Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Como .NET.NET Aspire projetos usam uma arquitetura conteinerizada, os bancos de dados são efêmeros e podem ser recriados a qualquer momento. Entity Framework Core (EF Core) usa um recurso chamado migrações para criar e atualizar esquemas de banco de dados. Como os bancos de dados são recriados quando o aplicativo é iniciado, você precisa aplicar migrações para inicializar o esquema de banco de dados sempre que o aplicativo for iniciado. Isso é feito registrando um projeto de serviço de migração em seu aplicativo que executa migrações durante a inicialização.
Neste tutorial, você aprenderá a configurar .NET Aspire projetos para executar migrações de EF Core durante a inicialização do aplicativo.
Pré-requisitos
Para trabalhar com .NET.NET Aspire, você precisa do seguinte instalado localmente:
- .NET 8,0 ou .NET 9,0
- Um ambiente de execução de contentor compatível com OCI, como:
- Docker Desktop ou Podman. Para obter mais informações, consulte Container runtime.
- Um ambiente de desenvolvedor integrado (IDE) ou editor de código, como:
- Visual Studio 2022 versão 17.9 ou superior (opcional)
-
Visual Studio Code (Opcional)
- C# Dev Kit: Extensão (Opcional)
- JetBrains Rider com o plugin .NET.NET Aspire (Opcional)
Para obter mais informações, consulte .NET.NET Aspire, e .
Obter a aplicação inicial
Este tutorial utiliza uma aplicação de exemplo que demonstra como aplicar migrações de EF Core em .NET Aspire. Use Visual Studio para clonar o aplicativo de exemplo do GitHub ou use o seguinte comando:
git clone https://github.com/MicrosoftDocs/aspire-docs-samples/
O aplicativo de exemplo está na pasta
- SupportTicketApi.Api: O projeto ASP.NET Core que hospeda a API.
- SupportTicketApi.AppHost: contém o host de app e a configuração.
- SupportTicketApi.Data: Contém os EF Core contextos e modelos.
- SupportTicketApi.ServiceDefaults: Contém as configurações de serviço padrão.
Execute o aplicativo para garantir que ele funcione conforme o esperado. No painel .NET.NET Aspire, aguarde até que todos os recursos estejam em execução e saudáveis. Em seguida, selecione o endpoint https do Swagger e teste o endpoint GET /api/SupportTickets da API expandindo a operação e selecionando Testar. Selecione Executar para enviar a solicitação e visualizar a resposta:
[
{
"id": 1,
"title": "Initial Ticket",
"description": "Test ticket, please ignore."
}
]
Feche as abas do navegador que exibem o endpoint Swagger e o dashboard .NET.NET Aspire e depois pare a depuração.
Criar migrações
Comece por criar algumas migrações para aplicar.
Abra um terminal (Ctrl+` no Visual Studio).
Defina SupportTicketApi\SupportTicketApi.Api como o diretório atual.
Use a ferramenta de linha de comando
dotnet ef
para criar uma nova migração para capturar o estado inicial do esquema de banco de dados:dotnet ef migrations add InitialCreate --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj
O comando seguinte:
- Executa a ferramenta de linha de comando de migração EF Core no diretório SupportTicketApi.Api.
dotnet ef
é executado neste local porque o serviço de API é onde o contexto de banco de dados é usado. - Cria uma migração chamada InitialCreate.
- Cria a migração na pasta Migrations no projeto SupportTicketApi.Data.
- Executa a ferramenta de linha de comando de migração EF Core no diretório SupportTicketApi.Api.
Modifique o modelo para que ele inclua uma nova propriedade. Abra SupportTicketApi.Data\Models\SupportTicket.cs e adicione uma nova propriedade à classe
SupportTicket
:namespace SupportTicketApi.Data.Models { public sealed class SupportTicket { public int Id { get; set; } [Required] public string Title { get; set; } = string.Empty; [Required] public string Description { get; set; } = string.Empty; public bool Completed { get; set; } } }
Crie outra nova migração para capturar as alterações no modelo:
dotnet ef migrations add AddCompleted --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj
Agora você tem algumas migrações para aplicar. Em seguida, você criará um serviço de migração que aplica essas migrações durante a inicialização do aplicativo.
Criar o serviço de migração
Para executar migrações, chame o EF CoreMigrate método ou o MigrateAsync método. Neste tutorial, você criará um serviço de trabalho separado para aplicar migrações. Essa abordagem separa as preocupações com a migração em um projeto dedicado, que é mais fácil de manter.
Para criar um serviço que aplique as migrações:
Adicione um novo projeto Worker Service à solução. Se estiver usando Visual Studio, clique com o botão direito do mouse na solução no Gerenciador de Soluções e selecione Add>New Project. Selecione Worker Service, nomeie o projeto SupportTicketApi.MigrationService e o destino .NET 8.0. Se estiver usando a linha de comando, use os seguintes comandos do diretório da solução:
dotnet new worker -n SupportTicketApi.MigrationService -f "net8.0" dotnet sln add SupportTicketApi.MigrationService
Adicione as referências de projeto SupportTicketApi.Data e SupportTicketApi.ServiceDefaults ao projeto SupportTicketApi.MigrationService usando Visual Studio ou a linha de comando:
dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.Data dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.ServiceDefaults
Adicione a referência ao pacote NuGet 📦Aspire.Microsoft.EntityFrameworkCore.SqlServer para o projeto SupportTicketApi.MigrationService utilizando o Visual Studio ou a linha de comando.
cd SupportTicketApi.MigrationService dotnet add package Aspire.Microsoft.EntityFrameworkCore.SqlServer -v "9.3.1"
Sugestão
Em alguns casos, talvez também seja necessário adicionar o pacote 📦 Microsoft.EntityFrameworkCore.Tools para evitar que EF Core falhe silenciosamente sem aplicar migrações. Isto é particularmente relevante quando se utilizam bases de dados diferentes de SQL Server, tais como PostgreSQL. Para obter mais informações, consulte dotnet/efcore#27215.
Adicione as linhas realçadas ao arquivo Program.cs no projeto SupportTicketApi.MigrationService:
using SupportTicketApi.Data.Contexts; using SupportTicketApi.MigrationService; var builder = Host.CreateApplicationBuilder(args); builder.AddServiceDefaults(); builder.Services.AddHostedService<Worker>(); builder.Services.AddOpenTelemetry() .WithTracing(tracing => tracing.AddSource(Worker.ActivitySourceName)); builder.AddSqlServerDbContext<TicketContext>("sqldata"); var host = builder.Build(); host.Run();
No código anterior:
- O método de extensão
AddServiceDefaults
adiciona a funcionalidade padrão de serviço. - O método de extensão
AddOpenTelemetry
configura a OpenTelemetry funcionalidade. - O método de extensão
AddSqlServerDbContext
adiciona o serviçoTicketContext
à coleção de serviços. Este serviço é usado para executar migrações e semear o banco de dados.
- O método de extensão
Substitua o conteúdo do arquivo Worker.cs no projeto SupportTicketApi.MigrationService com o seguinte código:
using System.Diagnostics; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using OpenTelemetry.Trace; using SupportTicketApi.Data.Contexts; using SupportTicketApi.Data.Models; namespace SupportTicketApi.MigrationService; public class Worker( IServiceProvider serviceProvider, IHostApplicationLifetime hostApplicationLifetime) : BackgroundService { public const string ActivitySourceName = "Migrations"; private static readonly ActivitySource s_activitySource = new(ActivitySourceName); protected override async Task ExecuteAsync(CancellationToken cancellationToken) { using var activity = s_activitySource.StartActivity("Migrating database", ActivityKind.Client); try { using var scope = serviceProvider.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<TicketContext>(); await RunMigrationAsync(dbContext, cancellationToken); await SeedDataAsync(dbContext, cancellationToken); } catch (Exception ex) { activity?.RecordException(ex); throw; } hostApplicationLifetime.StopApplication(); } private static async Task RunMigrationAsync(TicketContext dbContext, CancellationToken cancellationToken) { var strategy = dbContext.Database.CreateExecutionStrategy(); await strategy.ExecuteAsync(async () => { // Run migration in a transaction to avoid partial migration if it fails. await dbContext.Database.MigrateAsync(cancellationToken); }); } private static async Task SeedDataAsync(TicketContext dbContext, CancellationToken cancellationToken) { SupportTicket firstTicket = new() { Title = "Test Ticket", Description = "Default ticket, please ignore!", Completed = true }; var strategy = dbContext.Database.CreateExecutionStrategy(); await strategy.ExecuteAsync(async () => { // Seed the database await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken); await dbContext.Tickets.AddAsync(firstTicket, cancellationToken); await dbContext.SaveChangesAsync(cancellationToken); await transaction.CommitAsync(cancellationToken); }); }
No código anterior:
- O método
ExecuteAsync
é chamado quando o trabalhador inicia. Ele, por sua vez, executa as seguintes etapas:- Obtém uma referência ao serviço
TicketContext
do provedor de serviços. - Chama o
RunMigrationAsync
para aplicar todas as migrações pendentes. - Chama
SeedDataAsync
para semear o banco de dados com dados iniciais. - Interrompe o trabalhador com
StopApplication
.
- Obtém uma referência ao serviço
- Os métodos
RunMigrationAsync
eSeedDataAsync
encapsulam suas respetivas operações de banco de dados usando estratégias de execução para lidar com erros transitórios que podem ocorrer ao interagir com o banco de dados. Para saber mais sobre estratégias de execução, consulte Resiliência de Conexão.
- O método
Adicione o serviço de migração ao orquestrador
O serviço de migração é criado, mas precisa ser adicionado ao host do aplicativo .NET.NET Aspire para que seja executado quando o aplicativo for iniciado.
No projeto SupportTicketApi.AppHost, abra o arquivo Program.cs.
Adicione o seguinte código realçado:
var builder = DistributedApplication.CreateBuilder(args); var sql = builder.AddSqlServer("sql", port: 14329) .WithEndpoint(name: "sqlEndpoint", targetPort: 14330) .AddDatabase("sqldata"); var migrations = builder.AddProject<Projects.SupportTicketApi_MigrationService>("migrations") .WithReference(sql) .WaitFor(sql); builder.AddProject<Projects.SupportTicketApi_Api>("api") .WithReference(sql) .WithReference(migrations) .WaitForCompletion(migrations); builder.Build().Run();
Esse código alista o SupportTicketApi.MigrationService projeto como um serviço no host do .NET.NET Aspire aplicativo. Ele também garante que o recurso de API não seja executado até que as migrações sejam concluídas.
Observação
No código anterior, a chamada para AddDatabase adiciona uma representação de um SQL Server banco de dados ao .NET Aspire modelo de aplicativo com uma cadeia de conexão. Ele não cria um banco de dados no SQL Server contêiner. Para garantir que o banco de dados seja criado, o projeto de exemplo chama o método EF CoreEnsureCreated localizado no arquivo da API de tíquetes de suporte Program.cs.
Sugestão
O código cria o SQL Server contêiner cada vez que ele é executado e aplica migrações a ele. Os dados não persistem nas sessões de depuração e quaisquer novas linhas de banco de dados criadas durante o teste não sobreviverão a uma reinicialização do aplicativo. Se preferir manter esses dados, adicione um volume de dados ao seu contêiner. Para obter mais informações, consulte Adicionar SQL Server recurso com volume de dados.
Se o código não puder resolver o projeto de serviço de migração, adicione uma referência ao projeto de serviço de migração no projeto AppHost:
dotnet add SupportTicketApi.AppHost reference SupportTicketApi.MigrationService
Importante
Se você estiver usando Visual Studioe tiver selecionado a opção Enlist in Aspire orchestration ao criar o projeto Worker Service, um código semelhante será adicionado automaticamente com o nome do serviço
supportticketapi-migrationservice
. Substitua esse código pelo código anterior.
Remover o código de sementeação existente
Como o serviço de migração semeia o banco de dados, você deve remover o código de propagação de dados existente do projeto de API.
No projeto SupportTicketApi.Api, abra o arquivo Program.cs.
Exclua as linhas realçadas.
if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); using (var scope = app.Services.CreateScope()) { var context = scope.ServiceProvider.GetRequiredService<TicketContext>(); context.Database.EnsureCreated(); if(!context.Tickets.Any()) { context.Tickets.Add(new SupportTicket { Title = "Initial Ticket", Description = "Test ticket, please ignore." }); context.SaveChanges(); } } }
Testar o serviço de migração
Agora que o serviço de migração está configurado, execute o aplicativo para testar as migrações.
Execute o aplicativo e observe o painel SupportTicketApi.
Após uma breve espera, o estado do serviço
migrations
exibirá Concluído.Selecione o ícone Console logs no serviço de migração para investigar os logs que mostram os comandos SQL que foram executados.
Obter o código
Você pode encontrar o exemplo de aplicativo concluído em GitHub.
Mais código de exemplo
O aplicativo de exemplo Aspire Shop usa essa abordagem para aplicar migrações. Consulte o projeto AspireShop.CatalogDbManager
para a implementação do serviço de migração.