Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 44

Configuring PostgreSQL with Aspire.Npgsql

Recommended resources

Olá! Na lição anterior, você integrou um cache Redis à sua aplicação Aspire, aprendendo o padrão fundamental de dois passos: declarar um recurso no AppHost e consumi-lo em um serviço. Vimos como o nome do recurso é a chave que conecta os dois e como o Aspire Dashboard nos dá visibilidade sobre o comportamento da aplicação.

Hoje, vamos dar o próximo passo lógico: da gestão de dados voláteis (cache) para a persistência de dados. O objetivo desta lição é configurar um banco de dados PostgreSQL usando o componente Aspire.Npgsql. Você verá que o padrão de integração é exatamente o mesmo que aprendeu com o Redis, o que demonstra a consistência e a força do modelo de componentes do Aspire.

Vamos substituir um banco de dados de desenvolvimento, como o SQLite, por uma solução robusta e pronta para produção como o PostgreSQL, uma etapa crucial na criação de aplicações distribuídas.

O Padrão de Integração do Aspire: Revisitado

Lembre-se do padrão que usamos para o Redis:

  1. AppHost: Usamos um pacote Aspire.Hosting.* para declarar o contêiner Redis.
  2. Serviço (API): Usamos um pacote Aspire.* para configurar o cliente que se conecta ao Redis.
  3. Conexão: O nome do recurso e a chamada .WithReference() conectam os dois, injetando a connection string automaticamente.

Para o PostgreSQL, a abordagem é idêntica. Usaremos os pacotes:

  • Aspire.Hosting.PostgreSQL no projeto AppHost.
  • Aspire.Npgsql.EntityFrameworkCore.PostgreSQL no seu projeto de serviço (API) que usa Entity Framework Core.

A arquitetura que estamos construindo pode ser visualizada da seguinte forma:

Este diagrama mostra a relação que vamos estabelecer: o AppHost do Aspire orquestra uma Web API que, por sua vez, depende de um banco de dados PostgreSQL para persistência. O Aspire gerencia a conexão entre eles.


Passo 1: Configurar o Recurso PostgreSQL no AppHost

Assim como no Redis, o primeiro passo é declarar o nosso banco de dados como um recurso no Program.cs do AppHost. No entanto, com bancos de dados, temos um conceito novo e importante: recursos pai e filho. O servidor PostgreSQL é o recurso "pai", e o banco de dados específico dentro desse servidor é o recurso "filho".

Aspire PostgreSQL Entity Framework Core integration

A documentação oficial do Aspire é a melhor fonte para entender todas as opções disponíveis. Ela detalha como adicionar o servidor PostgreSQL, criar bancos de dados e configurar aspectos importantes como persistência.

Leia as seções "Hosting integration", "Add PostgreSQL server resource", "Add PostgreSQL pgAdmin resource" e "Add PostgreSQL server resource with data volume". Foco em como os métodos AddPostgres, AddDatabase, WithPgAdmin e WithDataVolume são encadeados para construir a configuração do recurso.

Para começar, adicione o pacote NuGet de hospedagem ao seu projeto AppHost:

dotnet add package Aspire.Hosting.PostgreSQL

Agora, vamos modificar o Program.cs do AppHost.

.NET Aspire Tutorial For Beginners

Este vídeo de Julio Casal é um guia prático excelente que demonstra a substituição de um banco de dados SQLite por PostgreSQL em uma aplicação Aspire. Ele cobre todos os detalhes que estamos discutindo.

Assista aos trechos de 15:36 a 27:45. Observe atentamente como ele: Adiciona o recurso do servidor PostgreSQL com builder.AddPostgres(). Adiciona um banco de dados específico como um recurso filho com .AddDatabase(). Configura a persistência de dados com .WithDataVolume(), um passo essencial para bancos de dados. Adiciona uma interface de gerenciamento com .WithPgAdmin() para facilitar o desenvolvimento.

Com base nos recursos, seu código no AppHost ficará parecido com isto:

// Em AppHost/Program.cs

var builder = DistributedApplication.CreateBuilder(args);

// 1. Adiciona o servidor PostgreSQL como um recurso pai, nomeado 'postgres'.
var postgresServer = builder.AddPostgres("postgres");

// 2. Adiciona um banco de dados específico, nomeado 'gamestoredb', como filho do servidor.
// Este nome ('gamestoredb') será a chave da sua connection string!
var database = postgresServer.AddDatabase("gamestoredb");

// 3. (Opcional, mas recomendado) Adiciona uma UI para gerenciar o banco de dados.
postgresServer.WithPgAdmin();

// 4. (Crucial) Persiste os dados do PostgreSQL usando um volume Docker.
postgresServer.WithDataVolume();

// Adiciona o projeto da API, que usará este banco de dados.
var apiService = builder.AddProject<Projects.MyAspireApp_ApiService>("apiservice")
                        // A referência será adicionada no Passo 3.
                        .WithReference(database);

// ...

builder.Build().Run();

Pontos-chave aqui:

  • AddPostgres("postgres"): Inicia um contêiner Docker com o servidor PostgreSQL. O nome "postgres" é um identificador interno para este recurso no Aspire.
  • AddDatabase("gamestoredb"): Cria um banco de dados chamado gamestoredb dentro do servidor PostgreSQL assim que o servidor estiver pronto. O nome que você fornece aqui é fundamental, pois ele se tornará o nome da sua connection string.
  • WithDataVolume(): Diferente do Redis (cache), onde a perda de dados ao reiniciar o contêiner pode ser aceitável, para um banco de dados, a persistência é obrigatória. Este método cria um volume Docker que sobrevive ao ciclo de vida do contêiner, garantindo que seus dados não sejam perdidos.
  • WithPgAdmin(): Similar ao WithRedisCommander da lição anterior, este método adiciona outro contêiner com uma interface web (pgAdmin) para você inspecionar e gerenciar seu banco de dados facilmente durante o desenvolvimento.

Passo 2: Configurar o Cliente EF Core no Serviço da API

Com o AppHost configurado, o próximo passo é instruir sua API a usar este banco de dados. Isso envolve substituir a configuração do DbContext do Entity Framework Core para usar o provedor Npgsql do Aspire.

Primeiro, adicione o pacote cliente ao seu projeto de API:

dotnet add package Aspire.Npgsql.EntityFrameworkCore.PostgreSQL

Agora, modifique o Program.cs da sua API para usar o novo método de extensão fornecido pelo Aspire.

.NET Aspire Tutorial For Beginners

Continuando no mesmo vídeo, esta seção mostra como modificar o código da API para se conectar ao novo banco de dados PostgreSQL gerenciado pelo Aspire.

Assista ao trecho de 30:48 a 34:24. O ponto principal é a substituição do método de registro do DbContext. Observe como ele remove a configuração antiga (para SQLite) e a substitui por uma única chamada a builder.AddNpgsqlDbContext().

A mudança no seu código será mais ou menos assim:

Antes (Exemplo com SQLite):

// Em ApiService/Program.cs

// ...
var connectionString = builder.Configuration.GetConnectionString("Games");
builder.Services.AddSqlite<GameStoreContext>(connectionString);
// ...

Depois (com Aspire e Npgsql):

// Em ApiService/Program.cs

var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();

// 1. Remove qualquer configuração de DbContext antiga.
// 2. Adiciona o DbContext usando o método de extensão do Aspire.
// O nome "gamestoredb" DEVE corresponder ao nome usado em AddDatabase() no AppHost.
builder.AddNpgsqlDbContext<GameStoreContext>("gamestoredb");

// ... (configuração de outros serviços)

var app = builder.Build();
// ...
app.Run();

É só isso! A chamada AddNpgsqlDbContext cuida de tudo:

  • Espera que o Aspire injete uma connection string chamada gamestoredb.
  • Configura o DbContext para usar o Npgsql com essa connection string.
  • Adiciona automaticamente health checks para verificar a saúde do banco de dados.
  • Configura a telemetria (logs, traces, métricas) para cada consulta ao banco de dados via OpenTelemetry.

Agora podemos remover a connection string do nosso arquivo appsettings.json, pois o Aspire irá gerenciá-la dinamicamente.


Passo 3: Conectar Tudo com WithReference

O último passo é fazer a conexão explícita no AppHost, exatamente como fizemos com o Redis.

// Em AppHost/Program.cs

// ...
var database = postgresServer.AddDatabase("gamestoredb");

var apiService = builder.AddProject<Projects.MyAspireApp_ApiService>("apiservice")
                        // Esta linha informa ao Aspire que 'apiService' depende do 'database'.
                        // Aspire injetará a connection string do 'database' no 'apiService'.
                        .WithReference(database);
// ...

O método WithReference(database) é o que aciona a "mágica": ele informa ao Aspire que o apiService precisa se conectar ao recurso database. Em tempo de execução, o Aspire irá gerar a connection string completa para o contêiner PostgreSQL e a injetará como uma variável de ambiente no processo do apiService, com o nome ConnectionStrings__gamestoredb. O método AddNpgsqlDbContext("gamestoredb") na API sabe como ler essa variável de ambiente.

Teste seu entendimento!

Você configurou seu AppHost com postgresServer.AddDatabase("production_db"), mas na sua API você chamou builder.AddNpgsqlDbContext<MyContext>("development_db"). O que acontecerá quando você iniciar a aplicação?

Show answer

A sua API irá falhar ao iniciar, provavelmente com uma exceção indicando que a connection string chamada development_db não pôde ser encontrada. O Aspire injetou a connection string com o nome production_db (correspondendo à chamada AddDatabase), mas o cliente EF Core na API está procurando por uma com o nome development_db. Os nomes devem ser idênticos para que a conexão funcione.


Verificação e Observabilidade

Agora, a melhor parte: ver tudo funcionando. Execute seu projeto AppHost e abra o Aspire Dashboard.

  1. Recursos no Dashboard: Você verá os novos recursos em execução: o contêiner postgres, seu filho gamestoredb e o contêiner postgres-pgadmin.
*Um exemplo do Aspire Dashboard mostrando os recursos PostgreSQL, Redis e os projetos da aplicação, todos orquestrados pelo Aspire.*

2. Verificar a Connection String: Clique no seu apiService no dashboard, vá para a aba de Environment variables e você encontrará a variável ConnectionStrings__gamestoredb com um valor totalmente preenchido.

  1. Explorar com pgAdmin: No dashboard, o recurso postgres-pgadmin terá um link de endpoint. Clique nele para abrir a interface do pgAdmin. Você verá que ele já está conectado ao seu servidor de banco de dados. Expanda os servidores e você encontrará seu gamestoredb. Se você usa EF Core Migrations, poderá ver que suas tabelas já foram criadas.

.NET Aspire Tutorial For Beginners

Para fechar o ciclo, vamos ver a verificação na prática. Este trecho final do vídeo mostra como usar o dashboard e o pgAdmin para confirmar que tudo está conectado e funcionando como esperado.

Assista ao trecho de 35:59 a 41:27. Preste atenção em como ele: Usa o dashboard para inspecionar as variáveis de ambiente injetadas na API. Abre o pgAdmin a partir do link do dashboard para verificar se as tabelas foram criadas pelas migrações do EF Core. Testa os endpoints da API para confirmar que os dados estão sendo lidos e escritos no banco de dados PostgreSQL.


Conclusão

Parabéns! Você substituiu com sucesso um banco de dados de desenvolvimento por PostgreSQL, usando o mesmo padrão consistente e poderoso do .NET Aspire que você aprendeu na lição anterior.

Principais Pontos:

  • A integração de componentes no Aspire segue um padrão consistente, seja para um cache como o Redis ou um banco de dados como o PostgreSQL.
  • O Aspire modela recursos de banco de dados como uma relação pai-filho (AddPostgres e AddDatabase).
  • O nome fornecido a AddDatabase() se torna a chave da connection string que conecta o AppHost e o serviço.
  • A persistência de dados é garantida com WithDataVolume(), um conceito crucial ao trabalhar com bancos de dados em contêineres.
  • Ferramentas como WithPgAdmin() e o Aspire Dashboard fornecem uma experiência de desenvolvimento local fantástica para verificação e observabilidade.

Com o cache e a persistência de dados dominados, estamos prontos para explorar outro pilar das arquiteturas distribuídas: a comunicação assíncrona.

Próxima lição: Na próxima aula, você aprenderá a integrar uma fila de mensagens RabbitMQ em uma aplicação Aspire, permitindo que seus serviços se comuniquem de forma desacoplada e resiliente.