Exercício – Usar serviços de banco de dados para persistir dados de um projeto do .NET Aspire

Concluído

Neste exercício, você substitui os armazenamentos de dados atuais para o aplicativo nativo de nuvem em desenvolvimento da sua empresa. No momento, o aplicativo usa um banco de dados SQLite armazenado localmente para dados de catálogo e um cache Redis na memória para carrinhos de compras do cliente. Substitua os armazenamentos de dados existentes por PostgreSQL e MongoDB.

Instalar pré-requisitos

Os pré-requisitos para o .NET Aspire são:

  • .NET 8
  • Visual Studio 2022 Preview
  • Docker Desktop ou Podman
  • Carga de trabalho do .NET Aspire no Visual Studio

Se você já tiver os pré-requisitos instalados, poderá pular para clonar o aplicativo existente.

Instalar o .NET 8

Siga esse link do .NET 8 e selecione o instalador correto para o seu sistema operacional. Por exemplo, se você estiver usando o Windows 11 e um processador moderno, selecione x64 .NET 8 SDK para Windows.

Após a conclusão do download, execute o instalador e siga as instruções. Em uma janela de terminal, execute o seguinte comando para verificar se a instalação foi bem-sucedida:

dotnet --version

Você deverá ver o número da versão do SDK do .NET instalado. Por exemplo:

8.0.300-preview.24203.14

Instale a versão prévia do Visual Studio 2022

Siga esse link Visual Studio 2022 Preview e selecione Baixar versão prévia. Após a conclusão do download, execute o instalador e siga as instruções.

Instalar o Docker Desktop

Siga esse link Docker Desktop e selecione o instalador correto para o seu sistema operacional. Após a conclusão do download, execute o instalador e siga as instruções.

Abra o aplicativo do Docker Desktop e aceite o contrato de serviço.

Instale a carga de trabalho do .NET Aspire no Visual Studio

Instale a carga de trabalho do .NET Aspire usando a CLI do .NET:

  1. Abra um terminal.

  2. Instale as cargas de trabalho do .NET Aspire com estes comandos:

    dotnet workload update
    dotnet workload install aspire
    dotnet workload list
    

    Você deverá ver os detalhes da carga de trabalho do .NET Aspire.

     Installed Workload Id      Manifest Version      Installation Source
    ---------------------------------------------------------------------------------------------
    aspire                     8.0.0/8.0.100         SDK 8.0.300-preview.24203, VS 17.10.34902.84
    
    Use `dotnet workload search` to find additional workloads to install.
    

Clonar e modificar o aplicativo Northern Mountains

Vamos usar git para obter o aplicativo Northern Mountains atual:

  1. Na linha de comando, navegue até uma pasta de sua escolha onde você possa trabalhar com o código.

  2. Execute o seguinte comando para clonar o aplicativo de amostra Northern Mountains eShop:

    git clone -b aspire-databases https://github.com/MicrosoftDocs/mslearn-aspire-starter
    
  3. Inicie o Visual Studio e selecione Abrir um projeto ou solução.

  4. Navegue até a pasta onde você clonou o eShop, abra a pasta iniciar e selecione o arquivo eShop.databases.sln e selecione Abrir.

  5. No Gerenciador de Soluções, expanda o projeto eShop.AppHost e abra Program.cs.

    // Databases
    
    var basketStore = builder.AddRedis("BasketStore").WithRedisCommander();
    
    // Identity Providers
    
    var idp = builder.AddKeycloakContainer("idp", tag: "23.0")
        .ImportRealms("../Keycloak/data/import");
    
    // DB Manager Apps
    
    builder.AddProject<Projects.Catalog_Data_Manager>("catalog-db-mgr");
    
    // API Apps
    
    var catalogApi = builder.AddProject<Projects.Catalog_API>("catalog-api");
    
    var basketApi = builder.AddProject<Projects.Basket_API>("basket-api")
            .WithReference(basketStore)
            .WithReference(idp);
    
    // Apps
    
    // Force HTTPS profile for web app (required for OIDC operations)
    var webApp = builder.AddProject<Projects.WebApp>("webapp")
        .WithReference(catalogApi)
        .WithReference(basketApi)
        .WithReference(idp, env: "Identity__ClientSecret");
    

    O código anterior mostra a configuração atual do aplicativo. O aplicativo usa um Cache Redis para o repositório de carrinhos.

  6. Explore o restante do aplicativo, concentre-se nos projetos Catalog.Data.Manager e Catalog.API e veja como eles usam um banco de dados SQLite armazenado localmente.

  7. Para iniciar o aplicativo, pressione F5 ou selecione Depuração > Iniciar Depuração.

  8. Se a caixa de diálogo Iniciar Docker Desktop aparecer, selecione Sim.

  9. Quando o dashboard do eShop .NET Aspire for exibido, para o recurso webapp, selecione o ponto de extremidade seguro:

    Uma captura de tela do dashboard do eShop .NET Aspire. O ponto de extremidade do aplicativo Web está realçado.

  10. O aplicativo é aberto em um navegador. Você pode explorar o aplicativo e ver como ele funciona.

    Uma captura de tela da home page do eShop.

    As credenciais do usuário de teste são test@example.com e P@$$w0rd1.

  11. Para interromper a depuração, pressione Shift+F5 ou selecione Depurar > Parar Depuração.

Adicionar um componente .NET Aspire PostgreSQL

A equipe responsável pelos microsserviços de catálogo criou o aplicativo para usar um banco de dados SQLite armazenado localmente. Essa abordagem é boa para o desenvolvimento, mas a equipe deseja usar um banco de dados mais robusto para produção.

Dois projetos se conectam ao banco de dados SQLite, o Catalog.Data.Manager e o Catalog.API. O gerenciador de dados é usado apenas para propagar o banco de dados com dados, portanto, você deve se concentrar no projeto Catalog.API.

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto Catalog.API, selecione Adicionar>Pacote do .NET Aspire.

  2. Na caixa Pesquisa, adicione Npgsql.EntityFramework ao final e pressione Enter.

  3. À esquerda, nos resultados, selecione Aspire.Npgsql.EntityFrameworkCore.PostgreSQL.

  4. À direita, selecione a lista suspensa de versão e selecione a última versão 8.0.0.

  5. Selecione Instalar.

  6. Se a caixa de diálogo Visualizar alterações aparecer, selecione Aplicar.

  7. Na caixa de diálogo Aceitação da Licença, selecione Aceito.

  8. No Gerenciador de Soluções, selecione o projeto Catalog.API para exibir o conteúdo do arquivo Catalog.API.csproj.

  9. Exclua o PackageReference para Microsoft.EntityFrameworkCore.Sqlite:

    <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.3" />
    

Registrar o novo DbContext do PostgreSQL

  1. No Gerenciador de Soluções, expanda o projeto Catalog.API e, em seguida, abra o arquivo Program.cs.

  2. Substitua o DbContext do SQLite:

    builder.Services.AddDbContext<CatalogDbContext>(
         options => options.UseSqlite(builder.Configuration.GetConnectionString("sqlconnection")
     	    ?? throw new InvalidOperationException(
     		    "Connection string 'sqlconnection' not found.")));
    

    Por um novo DbContext do PostgreSQL:

    builder.AddNpgsqlDbContext<CatalogDbContext>("CatalogDB");
    

    O aplicativo não precisa mais ler o arquivo Database.db. Portanto, remova as cadeias de caracteres associadas em appsettings.json.

  3. No Gerenciador de Soluções, em Catalog.API, selecione appsettings.json.

  4. Exclua as entradas de ConnectionStrings, o arquivo agora tem esta aparência:

    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "OpenApi": {
        "Endpoint": {
          "Name": "Catalog.API v1"
        },
        "Document": {
          "Description": "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample",
          "Title": "eShop - Catalog HTTP API",
          "Version": "v1"
        }
      },
      "CatalogOptions": {
        "PicBasePathFormat": "items/{0}/pic/"
      }
    }
    
    
  5. Clique com o botão direito do mouse no projeto Catalog.Data.Manager e selecione Remover.

  6. Na caixa de diálogo, selecione OK.

A equipe de banco de dados cria um backup de banco de dados PostgreSQL para você usar para criar e propagar o banco de dados do catálogo. Você pode exibir o backup na pasta Catalog.API/Seed.

Propagar o banco de dados PostgreSQL usando um volume associado

O projeto AppHost pode criar um contêiner de banco de dados PostgreSQL, semeá-lo com dados de um volume associado e, em seguida, por meio de referências de passagem de injeção de dependência para o Catalog.API.

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto eShop.AppHost, selecione Adicionar>Pacote do .NET Aspire.

  2. Na caixa Pesquisa, adicione PostgreSQL ao final e pressione Enter.

  3. À esquerda, nos resultados, selecione Aspire.Hosting.PostgreSQL.

  4. À direita, selecione a lista suspensa de versão e selecione a última versão 8.0.0.

  5. Selecione Instalar.

  6. Se a caixa de diálogo Visualizar alterações aparecer, selecione Aplicar.

  7. Na caixa de diálogo Aceitação da Licença, selecione Aceito.

  8. No Gerenciador de Soluções, expanda o projeto eShop.AppHost e, em seguida, abra o arquivo Program.cs.

  9. Abaixo do comentário //Databases, adicione o seguinte código:

    // Databases
    
    var basketStore = builder.AddRedis("BasketStore").WithRedisCommander();
    var postgres = builder.AddPostgres("postgres")
        .WithEnvironment("POSTGRES_DB", "CatalogDB")
        .WithBindMount("../Catalog.API/Seed", "/docker-entrypoint-initdb.d").WithPgAdmin();
    var catalogDB = postgres.AddDatabase("CatalogDB");
    

    O código anterior cria um contêiner de banco de dados PostgreSQL, adiciona um banco de dados chamado CatalogDB e associa o diretório /docker-entrypoint-initdb.d ao diretório ../Catalog.API/Seed. O código também cria um contêiner para a ferramenta pgAdmin que permite gerenciar o banco de dados PostgreSQL.

  10. Passe a referência catalogDB ao projeto Catalog.API adicionando .WithReference(catalogDB), o código agora é:

    // API Apps
    
    var catalogApi = builder.AddProject<Projects.Catalog_API>("catalog-api")
      .WithReference(catalogDB); 
    
  11. O projeto Catalog.Data.Manager não é mais necessário, portanto, remova o projeto do AppHost. Exclua este código:

    // DB Manager Apps
    
    builder.AddProject<Projects.Catalog_Data_Manager>("catalog-db-mgr");
    

Testar o aplicativo

O uso do .NET Aspire permitiu que sua equipe removesse um projeto inteiro. Além disso, a API de catálogo só precisa de uma única linha de código para adicionar o contexto do banco de dados PostgresSQL. A injeção de dependência e a descoberta de serviço do AppHost significam que nenhuma outra alteração de código é necessária para permitir que a API se conecte ao novo banco de dados.

  1. Compile e inicie o aplicativo, pressione F5 ou selecione Depurar > Iniciar Depuração.

    Uma captura de tela mostrando o dashboard atualizado do .NET Aspire, com os dois novos contêineres do PostgreSQL realçados.

    Há dois novos contêineres no painel que hospedam o servidor de banco de dados PostgreSQL e a ferramenta pgAdmin. Há também um recurso de banco de dados PostgreSQL que hospeda o banco de dados do CatalogDB.

  2. Use pgAdmin para se conectar ao banco de dados PostgreSQL e explorar os dados. Selecione o ponto de extremidade postgres pgadmin.

    Uma captura de tela da interface pgAdmin, realçando a navegação para a tabela Catálogo.

  3. Expanda Instâncias do Aspire>postgres>Bancos de Dados>CatalogDB>Esquemas>catálogo>Tabelas. Em seguida, clique com o botão direito do mouse na tabela Catálogo e selecione Ver/Editar Dados>Primeiras 100 Linhas.

  4. Você pode ver os dados carregados pelo AppHost.

    Uma captura de tela da interface pgAdmin mostrando as linhas retornadas da tabela Catálogo.

  5. Selecione a guia do dashboard Recursos de eShop no navegador e selecione o ponto de extremidade webapp.

  6. O aplicativo é aberto e funciona como antes.

  7. Para interromper a depuração, pressione Shift+F5 ou selecione Depurar > Parar Depuração.

Adicionar o componente .NET Aspire MongoDB ao aplicativo

O aplicativo atual usa o Redis como um armazenamento de dados na memória para o carrinho de compras de um cliente. A equipe deseja usar um armazenamento de dados mais robusto e durável para o carrinho. Substitua o cache Redis por um banco de dados do MongoDB.

Alterar a Basket.API para usar o MongoDB

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto Basket.API, selecione Adicionar e depois Adicionar>Pacote do .NET Aspire.
  2. Na caixa Search, insira MongoDB no final e pressione Enter.
  3. Selecione Aspire.MongoDB.Driver e, em seguida, selecione a versão mais recente 8.0.0.
  4. Selecione Instalar.
  5. Se a caixa de diálogo Visualizar alterações aparecer, selecione Aplicar.
  6. Na caixa de diálogo Aceitação da Licença, selecione Aceito.@

Criar um repositório de carrinhos do MongoDB

O microsserviço de carrinho usa HostingExtensions para gerenciar o armazenamento de dados do Redis. Substitua o repositório de dados do Redis por um armazenamento de dados do MongoDB.

  1. No Gerenciador de Soluções, expanda o projeto Basket.API e depois a pasta Storage. Em seguida, selecione o arquivo RedisBasketStore.cs.

    Há dois métodos assíncronos, GetBasketAsync e UpdateBasketAsync, que usam o Cache Redis. Permite criar versões do MongoDB desses métodos.

  2. No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Armazenamento e selecione Adicionar>Classe.

  3. Na caixa de diálogo Adicionar Novo Item, nomeie o arquivo MongoBasketStore.cs e selecione Adicionar.

  4. Substitua o código no arquivo MongoBasketStore.cs pelo seguinte código:

    using eShop.Basket.API.Models;
    using MongoDB.Driver;
    using MongoDB.Driver.Linq;
    
    namespace eShop.Basket.API.Storage;
    
    public class MongoBasketStore
    {
      private readonly IMongoCollection<CustomerBasket> _basketCollection;
    
      public MongoBasketStore(IMongoClient mongoClient)
      {
        // The database name needs to match the created database in the AppHost
        _basketCollection = mongoClient.GetDatabase("BasketDB").GetCollection<CustomerBasket>("basketitems");
      }
    
      public async Task<CustomerBasket?> GetBasketAsync(string customerId)
      {
        var filter = Builders<CustomerBasket>.Filter.Eq(r => r.BuyerId, customerId);
    
        return await _basketCollection.Find(filter).FirstOrDefaultAsync();
      }
    
      public async Task<CustomerBasket?> UpdateBasketAsync(CustomerBasket basket)
      {
        var filter = Builders<CustomerBasket>.Filter.Eq(r => r.BuyerId, basket.BuyerId);
    
        var result = await _basketCollection.ReplaceOneAsync(filter, basket, new ReplaceOptions { IsUpsert = true });
    
        return result.IsModifiedCountAvailable ? basket : null;
      }
    }
    

    O código anterior cria uma classe MongoBasketStore que funciona com o modelo de CustomerBasket. A coleção manipula as operações CRUD para as cestas de compras dos clientes em um banco de dados do MongoDB.

  5. No Gerenciador de Soluções, expanda Basket.API>Extensões e selecione o arquivo HostingExtensions.cs.

  6. Substitua o código Redis:

    builder.AddRedis("BasketStore");
    
    builder.Services.AddSingleton<RedisBasketStore>();
    

    Com o código do MongoDB:

    builder.AddMongoDBClient("BasketDB");
    
    builder.Services.AddSingleton<MongoBasketStore>();
    
  7. No Gerenciador de Soluções, expanda a pasta Grpc e, em seguida, abra o arquivo BasketService.cs.

  8. Altere a classe para aceitar um MongoBasketStore, substitua:

    public class BasketService(RedisBasketStore basketStore) : Basket.BasketBase
    

    Por:

    public class BasketService(MongoBasketStore basketStore) : Basket.BasketBase
    

Adicionar um banco de dados do MongoDB ao AppHost

  1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto eShop.AppHost e selecione Adicionar>Pacote do .NET Aspire.

  2. Na caixa Search, insira MongoDB no final e pressione Enter.

  3. Selecione o pacote Aspire.Hosting.MongoDB e selecione a versão mais recente 8.0.0.

  4. Selecione Instalar.

  5. Se a caixa de diálogo Visualizar alterações aparecer, selecione Aplicar.

  6. Na caixa de diálogo Aceitação da Licença, selecione Aceito.@

  7. No Gerenciador de Soluções, expanda o projeto eShop.AppHost e, em seguida, abra o arquivo Program.cs.

  8. Na seção Bancos de Dados, adicione um componente do MongoDB:

    var mongo = builder.AddMongoDB("mongo")
      .WithMongoExpress()
      .AddDatabase("BasketDB");
    

    O código anterior cria um contêiner de banco de dados do MongoDB, adiciona um banco de dados chamado BasketDB. O código também cria um contêiner para a ferramenta Mongo Express que permite gerenciar o banco de dados do MongoDB.

  9. Exclua o contêiner do Redis:

    var basketStore = builder.AddRedis("BasketStore").WithRedisCommander();
    

    Agora, o código deve se parecer com este:

    // Databases
    
    var postgres = builder.AddPostgres("postgres")
        .WithEnvironment("POSTGRES_DB", "CatalogDB")
        .WithBindMount("../Catalog.API/Seed", "/docker-entrypoint-initdb.d")
        .WithPgAdmin();
    var catalogDB = postgres.AddDatabase("CatalogDB");
    
    var mongo = builder.AddMongoDB("mongo")
        .WithMongoExpress()
        .AddDatabase("BasketDB");
    
  10. O projeto Basket.API precisa de uma referência ao novo banco de dados do MongoDB, e você deve remover a referência Redis:

    var basketApi = builder.AddProject<Projects.Basket_API>("basket-api")
            .WithReference(mongo)
            .WithReference(idp);
    

O projeto Basket.API agora está pronto para usar o banco de dados do MongoDB. Vamos testar o aplicativo para ver se ele funciona.

Testar o aplicativo

  1. Compile e inicie o aplicativo, pressione F5 ou selecione Depurar > Iniciar Depuração.

    Uma captura de tela do dashboard do .NET Aspire, com os contêineres do MongoDB realçados.

    Você pode ver os novos contêineres do MongoDB, um para o servidor de banco de dados e outro para o Mongo Express, no dashboard. Há também um novo recurso MongoDBDatabase que hospeda o banco de dados BasketDB.

  2. Selecione o ponto de extremidade webapp.

  3. Para entrar com as credenciais do usuário de teste, selecione o ícone de usuário no canto superior direito. O email é test@example.com e a senha é P@$$w 0rd1.

  4. Selecione o Adventurer GPS Watch na home page.

  5. Selecione Adicionar à sacola de compras. Você deverá ver uma exceção:

    Uma captura de tela mostrando o RpcException.

Depurar o aplicativo

O aplicativo está lançando uma exceção quando você tenta adicionar um item ao carrinho de compras. Você pode usar o dashboard para ajudar a depurar o problema.

  1. Selecione a guia do dashboard Recursos de eShop no navegador.

    Uma captura de tela do dashboard, os erros na Basket.API e no webapp são realçados.

    O painel mostra erros em basket-api e webapp. Analise os logos da basket-api.

  2. Para o recurso basket-api, na coluna Logs, selecione Exibir.

    Uma captura de tela dos logs do serviço basket-api.

    Há uma exceção:

    System.FormatException: Element '_id' does not match any field or property of class eShop.Basket.API.Models.CustomerBasket.
    
  3. Selecione o item de menu Recursos e depois o ponto de extremidade mongo-mongoexpress.

  4. Na seção Bancos de dados, ao lado de BasketDB, selecione Exibir.

  5. Em Coleções, ao lado de basketitems, selecione Exibir.

    Uma captura de tela do Mongo Express mostrando os dados armazenados na coleção de basketitems.

    Os documentos armazenados em um MongoDB têm um campo _id. Cada documento armazenado em uma coleção do MongoDB deve ter um campo de _id exclusivo.

  6. Para interromper a depuração, pressione Shift+F5 ou selecione Depurar > Parar Depuração.

Examine o código e corrija o problema

Vamos examinar o CustomerBasket e ver se podemos encontrar o problema.

  1. No Gerenciador de Soluções, expanda a pasta Basket.API>Modelos e abra o arquivo CustomerBasket.cs.

    public class CustomerBasket
    {
        public required string BuyerId { get; set; }
    
        public List<BasketItem> Items { get; set; } = [];
    }
    

    O modelo CustomerBasket não tem um campo nem uma propriedade que corresponda ao campo _id. A Entity Framework está tentando mapear o campo _id para o modelo CustomerBasket e não consegue encontrar uma correspondência.

  2. Atualize o modelo CustomerBasket para incluir um campo _id:

    public class CustomerBasket
    {
        /// <summary>
        /// MongoDB document identifier
        /// </summary>
        public string _id { get; set; } = "";
    
        public required string BuyerId { get; set; }
    
        public List<BasketItem> Items { get; set; } = [];
    }
    

Testar o aplicativo fixo

  1. Para compilar e iniciar o aplicativo, pressione F5 ou selecione Depurar > Iniciar Depuração.

  2. Para webapp, na coluna Pontos de extremidade, clique com o botão direito do mouse na URL e selecione Abrir link na janela InPrivate.

    O uso de uma janela InPrivate garante que o navegador não use o cookie de sessão anterior para autenticação.

  3. Para entrar com as credenciais do usuário de teste, selecione o ícone de usuário no canto superior direito. O email é test@example.com e a senha é P@$$w 0rd1.

  4. Selecione o Adventurer GPS Watch na home page.

  5. Selecione Adicionar à sacola de compras.

    Uma captura de tela do carrinho de compras do eShop funcionando.

    A funcionalidade do carrinho do aplicativo Northern Mountain agora está funcionando.

Você substituiu com êxito o banco de dados SQLite por um banco de dados PostgreSQL e o Cache Redis por um banco de dados MongoDB. Você usou o .NET Aspire para gerenciar os bancos de dados e explorar os dados neles e usou o dashboard para ajudar a depurar um problema com o aplicativo.