Exercício - Use serviços de banco de dados para persistir dados de um projeto .NET Aspire

Concluído

Neste exercício, você substitui os armazenamentos de dados atuais pelo aplicativo nativo da 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 as cestas de compras do cliente. Você substitui os armazenamentos de dados existentes por PostgreSQL e MongoDB.

Pré-requisitos da instalação

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

  • .NET 8
  • Pré-visualização do Visual Studio 2022
  • 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 este link do .NET 8 e selecione o instalador correto para seu sistema operacional. Por exemplo, se você estiver usando o Windows 11 e um processador moderno, selecione o x64 .NET 8 SDK para Windows.

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

dotnet --version

Você deve ver o número da versão do SDK do .NET que você instalou. Por exemplo:

8.0.300-preview.24203.14

Instalar o Visual Studio 2022 Preview

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

Instalar o Docker Desktop

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

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

Instalar 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ê deve 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.
    

Clone e modifique o aplicativo Northern Mountains

Vamos usar git para obter o aplicativo atual Northern Mountains:

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

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

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

  4. Navegue até a pasta onde você clonou a eShop, abra a pasta inicial e selecione o arquivo eShop.databases.sln e, em seguida, 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 a loja de cestos.

  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 Depurar > Iniciar Depuração.

  8. Se a caixa de diálogo Iniciar área de trabalho do Docker for exibida, selecione Sim.

  9. Quando o painel do eShop .NET Aspire aparecer, para o recurso webapp , selecione o ponto de extremidade seguro:

    Uma captura de tela do painel do eShop .NET Aspire . O ponto de extremidade do webapp é realçado.

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

    Uma captura de ecrã da página inicial da eShop.

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

  11. Para parar 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 do catálogo criou o aplicativo para usar um banco de dados SQLite armazenado localmente. Essa abordagem é boa para o desenvolvimento, mas a equipe quer usar um banco de dados mais robusto para produção.

Dois projetos se conectam ao banco de dados SQLite, os projetos Catalog.Data.Manager e Catalog.API . O gerenciador de dados é usado apenas para semear 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 Pacote Add.NET> Aspire .

  2. Na caixa Pesquisar, 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, em seguida, selecione a versão 8.0.0 mais recente.

  5. Selecione Instalar.

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

  7. Na caixa de diálogo Aceitação de 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 PostgreSQL DbContext

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

  2. Substitua o SQLite DbContext:

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

    Com o novo PostgreSQL DbContext:

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

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

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

  4. Exclua as ConnectionStrings entradas, 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 semear o banco de dados de catálogo. Você pode exibir o backup na pasta Catalog.API/Seed .

Semear o banco de dados PostgreSQL usando um volume acoplado

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

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

  2. Na caixa Pesquisar, 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, em seguida, selecione a versão 8.0.0 mais recente.

  5. Selecione Instalar.

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

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

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

  9. Sob o //Databases comentário, 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 vincula o diretório /docker-entrypoint-initdb.d ao .. /Catalog.API/Diretório 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 catalogDB referência para o 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 a aplicação

A utilização do .NET Aspire permitiu à sua equipa remover 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 painel atualizado do .NET Aspire com os dois novos contêineres 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 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, destacando 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 Exibir/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 painel de recursos da eShop em seu navegador e, em seguida, selecione o ponto de extremidade do webapp.

  6. O aplicativo abre e funciona como antes.

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

Adicione o componente .NET Aspire MongoDB ao aplicativo

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

Altere o 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, em seguida, selecione Pacote Add.NET> Aspire .
  2. Na caixa Pesquisar, digite MongoDB no final e pressione Enter.
  3. Selecione o Aspire.MongoDB.Driver e, em seguida, selecione a versão 8.0.0 mais recente.
  4. Selecione Instalar.
  5. Se a caixa de diálogo Visualizar alterações for exibida, selecione Aplicar.
  6. Na caixa de diálogo Aceitação de licença , selecione Aceito. @

Criar uma loja de cestas MongoDB

O microsserviço de cesta usa HostingExtensions para gerenciar o armazenamento de dados Redis. Substitua o armazenamento de dados Redis por um armazenamento de dados MongoDB.

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

    Há dois métodos GetBasketAsync assíncronos e UpdateBasketAsync, que usam o cache Redis. Vamos 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 com o 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 MongoBasketStore classe que funciona com o CustomerBasket modelo. A coleção lida com as operações CRUD para as cestas de compras dos clientes em um banco de dados MongoDB.

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

  6. Substitua o código Redis:

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

    Com o código MongoDB:

    builder.AddMongoDBClient("BasketDB");
    
    builder.Services.AddSingleton<MongoBasketStore>();
    
  7. No Gerenciador de Soluções, expanda a pasta Grpc e 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 MongoDB ao AppHost

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

  2. Na caixa Pesquisar, digite MongoDB no final e pressione Enter.

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

  4. Selecione Instalar.

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

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

  7. No Gerenciador de Soluções, expanda o projeto eShop.AppHost e 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 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 MongoDB.

  9. Exclua o contêiner Redis:

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

    O código agora deve ter esta aparência:

    // 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 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 MongoDB. Vamos testar o aplicativo para ver se ele funciona.

Testar a aplicação

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

    Uma captura de tela do painel 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 painel. Há também um novo recurso MongoDBDatabase que hospeda o banco de dados BasketDB.

  2. Selecione o ponto de extremidade do webapp .

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

  4. Selecione o Relógio GPS Aventureiro na página inicial.

  5. Selecione Adicionar ao carrinho de compras, você 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 painel para ajudar a depurar o problema.

  1. Selecione a guia do painel de recursos da eShop no seu navegador.

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

    O painel mostra erros na cesta-api e no webapp. Revise os logs para a basket-api.

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

    Uma captura de tela dos logs para o 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, em seguida, selecione o ponto de extremidade mongo-mongoexpress .

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

  5. Em Coleções, ao lado de itens da cesta, selecione Exibir.

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

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

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

Revise o código e corrija o problema

Vamos olhar para o CustomerBasket, e ver se podemos encontrar o problema.

  1. No Gerenciador de Soluções, expanda a pasta Modelos Basket.API> 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 ou propriedade que corresponda ao campo _id . A estrutura de entidade está tentando mapear o campo _id para o modelo CustomerBasket e não consegue encontrar uma correspondência.

  2. Atualize o CustomerBasket modelo 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 o aplicativo Web, 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 de usuário de teste, selecione o ícone de usuário no canto superior direito. O e-mail é test@example.com e a senha é P@$$w 0rd1.

  4. Selecione o Relógio GPS Aventureiro na página inicial.

  5. Selecione Adicionar ao carrinho de compras.

    Uma captura de ecrã do cesto de compras da eShop a funcionar.

    A funcionalidade de cesta de aplicativos Northern Mountains 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 painel para ajudar a depurar um problema com o aplicativo.