Ejercicio: Uso de servicios de base de datos para conservar datos de un proyecto de .NET Aspire

Completado

En este ejercicio, reemplazará los almacenes de datos actuales de la aplicación nativa de nube en desarrollo de su empresa. En este momento, la aplicación usa una base de datos de SQLite almacenada localmente para los datos del catálogo y una caché en memoria de Redis para los carros de la compra del cliente. Reemplazará los almacenes de datos existentes por PostgreSQL y MongoDB.

Requisitos previos de instalación

Los requisitos previos para .NET Aspire son:

  • .NET 8
  • Versión preliminar de Visual Studio 2022
  • Docker Desktop o Podman
  • Carga de trabajo de .NET Aspire en Visual Studio

Si ya tiene instalados los requisitos previos, puede pasar directamente a clonar la aplicación existente.

Instalación de .NET 8

Siga este vínculo de .NET 8 y seleccione el instalador correcto para el sistema operativo. Por ejemplo, si usa Windows 11 y un procesador moderno, seleccione el SDK de .NET 8 para Windows x64.

Una vez completada la descarga, ejecute el instalador y siga las instrucciones. En una ventana de terminal, ejecute el siguiente comando para comprobar que la instalación se realizó correctamente:

dotnet --version

Debería ver el número de versión del SDK de .NET que instaló. Por ejemplo:

8.0.300-preview.24203.14

Instalación de Visual Studio 2022 Preview

Siga este vínculo de Visual Studio 2022 Preview y seleccione Descargar versión preliminar. Una vez completada la descarga, ejecute el instalador y siga las instrucciones.

Instalar Docker Desktop

Siga este vínculo de Docker Desktop y seleccione el instalador correcto para el sistema operativo. Una vez completada la descarga, ejecute el instalador y siga las instrucciones.

Abra la aplicación Docker Desktop y acepte el contrato de servicio.

Instalación de la carga de trabajo de .NET Aspire en Visual Studio

Instale la carga de trabajo de .NET Aspire mediante la CLI de NET:

  1. Abra un terminal.

  2. Instale las cargas de trabajo de .NET Aspire con estos comandos:

    dotnet workload update
    dotnet workload install aspire
    dotnet workload list
    

    Debería ver los detalles de la carga de trabajo de .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.
    

Clonación y modificación de la aplicación Northern Mountains

Vamos a usar git para obtener la aplicación Northern Mountains actual:

  1. En la línea de comandos, vaya a una carpeta de su elección, donde pueda trabajar con código.

  2. Ejecute el siguiente comando para clonar la aplicación de ejemplo Northern Mountains eShop:

    git clone -b aspire-databases https://github.com/MicrosoftDocs/mslearn-aspire-starter
    
  3. Inicie Visual Studio y después seleccione Abrir un proyecto o solución.

  4. Vaya a la carpeta donde ha clonado eShop, abra la carpeta de inicio y seleccione el archivo eShop.databases.sln y, a continuación, seleccione Abrir.

  5. En el Explorador de soluciones, expanda el proyecto eShop.AppHost y, después, abra el archivo 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");
    

    El código anterior muestra la configuración actual de la aplicación. La aplicación usa Redis Cache para el almacenamiento de los carros de la compra.

  6. Explore el resto de la aplicación, céntrese en los proyectos Catalog.Data.Manager y Catalog.API y vea cómo usan una base de datos de SQLite almacenada localmente.

  7. Para iniciar la aplicación, presione F5 o seleccione Depurar > Iniciar depuración.

  8. Si aparece el cuadro de diálogo Iniciar Docker Desktop, seleccione .

  9. Cuando aparezca el panel eShop de .NET Aspire, del recurso de aplicación web, seleccione el punto de conexión seguro:

    Captura de pantalla del panel eShop de .NET Aspire. El punto de conexión de la aplicación web está resaltado.

  10. La aplicación se abre en un explorador. Puede explorar la aplicación y ver cómo funciona.

    Captura de pantalla de la página principal de eShop.

    Las credenciales de usuario de prueba son test@example.com y P@$$w0rd1.

  11. Para detener la depuración, presione Mayús+F5 o seleccione Depurar > Detener depuración.

Adición de un componente de PostgreSQL para .NET Aspire

El equipo responsable de los microservicios de catálogo creó la aplicación para que usara una base de datos de SQLite almacenada localmente. Este enfoque es adecuado para el desarrollo, pero el equipo quiere usar una base de datos más sólida para producción.

Dos proyectos se conectan a la base de datos de SQLite: Catalog.Data.Manager y Catalog.API. Data Manager solo se usa para proveer la base de datos con datos, por lo que debe centrarse en el proyecto Catalog.API.

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto Catalog.API, seleccione Agregar>Paquete de .NET Aspire.

  2. En el cuadro Buscar, agregue Npgsql.EntityFramework al final y presione Entrar.

  3. A la izquierda, en los resultados, seleccione Aspire.Npgsql.EntityFrameworkCore.PostgreSQL.

  4. A la derecha, seleccione la lista desplegable de versiones y, a continuación, seleccione la versión más reciente: 8.0.0.

  5. Seleccione Instalar.

  6. Si aparece el cuadro de diálogo Vista previa de cambios, seleccione Aplicar.

  7. En el diálogo Aceptación de licencia, seleccione Acepto.

  8. En el Explorador de soluciones, seleccione el proyecto Catalog.API para ver el contenido del archivo Catalog.API.csproj.

  9. Elimine PackageReference de Microsoft.EntityFrameworkCore.Sqlite:

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

Registro del nuevo PostgreSQL DbContext

  1. En el Explorador de soluciones, expanda el proyecto Catalog.API y, a continuación, abra el archivo Program.cs.

  2. Reemplace SQLite DbContext:

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

    Por el nuevo PostgreSQL DbContext:

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

    La aplicación ya no necesita leer el archivo Database.db, por lo que elimina las cadenas asociadas en appsettings.json.

  3. En el Explorador de soluciones, en Catalog.API, seleccione appsettings.json.

  4. Elimine las entradas ConnectionStrings y el archivo tendrá el siguiente aspecto:

    {
      "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. Haga clic con el botón derecho en el proyecto Catalog.Data.Manager y seleccione Eliminar.

  6. En el cuadro de diálogo, seleccione Aceptar.

El equipo de base de datos crea una copia de seguridad de la base de datos de PostgreSQL que se usará para crear y proveer la base de datos del catálogo. Puede ver la copia de seguridad en la carpeta Catalog.API/Seed.

Proveer la base de datos de PostgreSQL mediante un volumen limitado

El proyecto AppHost puede crear un contenedor de bases de datos de PostgreSQL, proporcionarle datos de un volumen limitado y, a continuación, pasar referencias mediante la inserción de dependencias en el proyecto Catalog.API.

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto eShop.AppHost, seleccione Agregar>Paquete de .NET Aspire.

  2. En el cuadro de búsqueda, agregue PostgreSQL al final y presione Entrar.

  3. A la izquierda, en los resultados, seleccione Aspire.Hosting.PostgreSQL.

  4. A la derecha, seleccione la lista desplegable de versiones y, a continuación, seleccione la versión más reciente: 8.0.0.

  5. Seleccione Instalar.

  6. Si aparece el cuadro de diálogo Vista previa de cambios, seleccione Aplicar.

  7. En el diálogo Aceptación de licencia, seleccione Acepto.

  8. En el Explorador de soluciones, expanda el proyecto eShop.AppHost y, a continuación, abra el archivo Program.cs.

  9. En el comentario //Databases, agregue el código siguiente:

    // 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");
    

    El código anterior crea un contenedor de bases de datos de PostgreSQL, agrega una base de datos denominada CatalogDB y enlaza el directorio /docker-entrypoint-initdb.d al directorio ../Catalog.API/Seed. El código también crea un contenedor para la herramienta pgAdmin que le permite administrar la base de datos de PostgreSQL.

  10. Pase la referencia catalogDB al proyecto Catalog.API agregando .WithReference(catalogDB). El código ahora es:

    // API Apps
    
    var catalogApi = builder.AddProject<Projects.Catalog_API>("catalog-api")
      .WithReference(catalogDB); 
    
  11. El proyecto Catalog.Data.Manager ya no es necesario, por lo que puede eliminarlo de AppHost. Elimine este código:

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

Prueba de la aplicación

El uso de .NET Aspire permitió al equipo eliminar un proyecto completo. Además, la API de catálogo solo necesita una única línea de código para agregar el contexto de base de datos de PostgresSQL. La inserción de dependencias y la detección de servicios de AppHost significa que no se necesitan otros cambios de código para permitir que la API se conecte a la nueva base de datos.

  1. Compile e inicie la aplicación, presione F5 o seleccione Depurar > Iniciar depuración.

    Captura de pantalla que muestra el panel de .NET Aspire actualizado, con los dos nuevos contenedores de PostgreSQL resaltados.

    Hay dos contenedores nuevos en el panel que hospedan el servidor de bases de datos de PostgreSQL y la herramienta pgAdmin. También hay un recurso de base de datos de PostgreSQL que hospeda la base de datos CatalogDB.

  2. Use pgAdmin para conectarse a la base de datos de PostgreSQL y explorar los datos. Seleccione el punto de conexión postgres pgadmin.

    Captura de pantalla de la interfaz pgAdmin, en la que se resalta la navegación a la tabla Catalog.

  3. Expanda Instancias de Aspire>postgres>Bases de datos>CatalogDB>Esquemas>catalog>Tablas. A continuación, haga clic con el botón derecho en la tabla Catalog y seleccione Ver y editar datos>Primeras 100 filas.

  4. Puede ver los datos cargados por AppHost.

    Captura de pantalla de la interfaz pgAdmin, en la que se muestran las filas devueltas de la tabla Catalog.

  5. Seleccione la pestaña Recursos de eShop del panel en el explorador y, a continuación, seleccione el punto de conexión webapp.

  6. La aplicación se abre y funciona como antes.

  7. Para detener la depuración, presione Mayús+F5 o seleccione Depurar > Detener depuración.

Adición del componente MongoDB de .NET Aspire a la aplicación

La aplicación actual usa Redis como almacén de datos en memoria para el carro de la compra de un cliente. El equipo quiere usar un almacén de datos más sólido y duradero para el carro de la compra. Reemplace Redis Cache por una base de datos de MongoDB.

Cambio de Basket.API para que use MongoDB

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto Basket.API, seleccione Agregar y, a continuación, seleccioneAgregar>Paquete de .NET Aspire.
  2. En el cuadro de búsqueda, escriba MongoDB al final y presione Entrar.
  3. Seleccione Aspire.MongoDB.Driver y, después, seleccione la versión más reciente: 8.0.0.
  4. Seleccione Instalar.
  5. Si aparece el cuadro de diálogo Vista previa de cambios, seleccione Aplicar.
  6. En el cuadro de diálogo Aceptación de licencia, seleccione Acepto.

Creación de un almacén de carros de la compra de MongoDB

El microservicio de carros de la compra usa HostingExtensions para administrar el almacén de datos de Redis. Reemplace el almacén de datos de Redis por un almacén de datos de MongoDB.

  1. En el Explorador de soluciones, expanda el proyecto Basket.API, después la carpeta Storage y, finalmente, seleccione el archivo RedisBasketStore.cs.

    Hay dos métodos asincrónicos, GetBasketAsync y UpdateBasketAsync, que usan Redis Cache. Vamos a crear versiones de MongoDB de estos métodos.

  2. En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Almacenamiento y seleccione Agregar>Clase.

  3. En el cuadro de diálogo Agregar nuevo elemento, asigne el nombre MongoBasketStore.cs al archivo y, a continuación, seleccione Agregar.

  4. Reemplace el código del archivo MongoBasketStore.cs por el código siguiente:

    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;
      }
    }
    

    El código anterior crea una clase MongoBasketStore que funciona con el modelo CustomerBasket. La colección controla las operaciones CRUD de los carros de la compra de los clientes en una base de datos de MongoDB.

  5. En el Explorador de soluciones, expanda Basket.API>Extensiones y, a continuación, seleccione el archivo HostingExtensions.cs.

  6. Reemplace el código de Redis:

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

    Con el código de MongoDB:

    builder.AddMongoDBClient("BasketDB");
    
    builder.Services.AddSingleton<MongoBasketStore>();
    
  7. En el Explorador de soluciones, expanda la carpeta Grpc y abra el archivo BasketService.cs.

  8. Cambie la clase para aceptar un MongoBasketStore y reemplace:

    public class BasketService(RedisBasketStore basketStore) : Basket.BasketBase
    

    Por:

    public class BasketService(MongoBasketStore basketStore) : Basket.BasketBase
    

Adición de una base de datos de MongoDB a AppHost

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto eShop.AppHost y seleccione Agregar>Paquete de .NET Aspire.

  2. En el cuadro de búsqueda, escriba MongoDB al final y presione Entrar.

  3. Seleccione el paquete Aspire.Hosting.MongoDB y, después, seleccione la versión más reciente: 8.0.0.

  4. Seleccione Instalar.

  5. Si aparece el cuadro de diálogo Vista previa de cambios, seleccione Aplicar.

  6. En el cuadro de diálogo Aceptación de licencia, seleccione Acepto.

  7. En el Explorador de soluciones, expanda el proyecto eShop.AppHost y, a continuación, abra el archivo Program.cs.

  8. En la sección Bases de datos, agregue un componente de MongoDB:

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

    El código anterior crea un contenedor de bases de datos de MongoDB y agrega una base de datos denominada BasketDB. El código también crea un contenedor para la herramienta Mongo Express que le permite administrar la base de datos de MongoDB.

  9. Elimine el contenedor de Redis:

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

    El código ahora debería presentar un aspecto similar a 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. El proyecto Basket.API necesita una referencia a la nueva base de datos de MongoDB y debe quitar la referencia de Redis:

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

El proyecto Basket.API ya está listo para usar la base de datos de MongoDB. Vamos a probar la aplicación para ver si funciona.

Prueba de la aplicación

  1. Compile e inicie la aplicación, presione F5 o seleccione Depurar > Iniciar depuración.

    Captura de pantalla del panel de .NET Aspire, con los contenedores de MongoDB resaltados.

    Puede ver los nuevos contenedores de MongoDB, uno para el servidor de bases de datos el otro para Mongo Express, en el panel. También hay un nuevo recurso MongoDBDatabase que hospeda la base de datos BasketDB.

  2. Seleccione el punto de conexión webapp.

  3. Para iniciar sesión con las credenciales del usuario de prueba, seleccione el icono de usuario en la parte superior derecha. El correo electrónico es test@example.com y la contraseña es P@$$w0rd1.

  4. Seleccione el producto Reloj GPS Adventurer en la página principal.

  5. Seleccione Agregar a cesta de compras y debería aparecer una excepción:

    Captura de pantalla que muestra la excepción RpcException.

Depurar la aplicación

La aplicación produce una excepción al intentar agregar un artículo al carro de la compra. Puede usar el panel para ayudar a depurar el problema.

  1. Seleccione la pestaña Recursos de eShop del panel en el explorador.

    Captura de pantalla del panel, se resaltan los errores en Basket.API y webapp.

    El panel muestra errores en basket-api y webapp. Revise los registros de basket-api.

  2. Para el recurso basket-api, de la columna Logs, seleccione Ver.

    Captura de pantalla de los registros del servicio basket-api.

    Hay una excepción:

    System.FormatException: Element '_id' does not match any field or property of class eShop.Basket.API.Models.CustomerBasket.
    
  3. Seleccione el elemento de menú Recursos y, a continuación, seleccione el punto de conexión mongo-mongoexpress.

  4. En la sección Bases de datos, junto a BasketDB, seleccione Ver.

  5. En Colecciones, junto a basketitems, seleccione Ver.

    Captura de pantalla de Mongo Express que muestra los datos almacenados en la colección basketitems.

    Los documentos almacenados en MongoDB tienen un campo _id. Todos los documentos almacenados en una colección de MongoDB deben tener un campo _id único.

  6. Para detener la depuración, presione Mayús+F5 o seleccione Depurar > Detener depuración.

Revisión del código y corrección del problema

Echemos un vistazo a CustomerBasket y veamos si podemos encontrar el problema.

  1. En el Explorador de soluciones, expanda la carpeta Basket.API>Modelos y abra el archivo CustomerBasket.cs.

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

    El modelo CustomerBasket no tiene un campo o propiedad que coincida con el campo _id. Entity Framework está intentando asignar el campo _id al modelo CustomerBasket y no puede encontrar ninguna coincidencia.

  2. Actualice el modelo CustomerBasket para que incluya un 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; } = [];
    }
    

Probar la aplicación corregida

  1. Para compilar e iniciar la aplicación, presione F5 o seleccione Depurar > Iniciar depuración.

  2. Para webapp, en la columna Endpoints, haga clic con el botón derecho en la dirección URL y, a continuación, seleccione Abrir vínculo en la ventana InPrivate.

    El uso de una ventana InPrivate garantiza que el explorador no use la cookie de la sesión anterior para la autenticación.

  3. Para iniciar sesión con las credenciales del usuario de prueba, seleccione el icono de usuario en la parte superior derecha. El correo electrónico es test@example.com y la contraseña es P@$$w0rd1.

  4. Seleccione el producto Reloj GPS Adventurer en la página principal.

  5. Seleccione Agregar a la cesta de compras.

    Captura de pantalla del funcionamiento de la cesta de la compra de eShop.

    La funcionalidad de carro de la compra de la aplicación Northern Mountains ahora funciona.

Ha reemplazado correctamente la base de datos de SQLite por una base de datos de PostgreSQL y la instancia de Redis Cache por una base de datos de MongoDB. Ha usado .NET Aspire para administrar las bases de datos y explorar los datos que hay en ellas y ha usado el panel para ayudar a depurar un problema con la aplicación.