Ejercicio: Acceso a datos desde un componente de Blazor

Completado

Las pizzas actuales codificadas de forma rígida de la aplicación deben reemplazarse por una base de datos. Puede usar Microsoft Entity Framework para agregar conexiones a orígenes de datos. En nuestra aplicación, usaremos una base de datos SQLite para almacenar nuestras pizzas.

En este ejercicio, agregará paquetes para admitir la funcionalidad de base de datos, conectará las clases a una base de datos de back-end y agregará una clase auxiliar para cargar previamente los datos de las pizzas de las empresas.

Adición de paquetes para admitir el acceso a la base de datos

  1. Detenga la aplicación si todavía está en ejecución.

  2. En Visual Studio Code seleccione Terminal>Nuevo terminal.

  3. En el nuevo terminal, establezca la ubicación en el directorio BlazingPizza.

    cd BlazingPizza
    
  4. Ejecute estos comandos para agregar los paquetes Microsoft.EntityFrameworkCore, Microsoft.EntityFrameworkCore.Sqlite y System.Net.Http.Json:

    dotnet add package Microsoft.EntityFrameworkCore --version 6.0.8
    dotnet add package Microsoft.EntityFrameworkCore.Sqlite --version 6.0.8
    dotnet add package System.Net.Http.Json --version 6.0.0
    

    Estos comandos agregan referencias de paquete al archivo BlazingPizza.csproj:

      <ItemGroup>
        <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
        <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.8" />
        <PackageReference Include="System.Net.Http.Json" Version="6.0.0" />
      </ItemGroup>
    

Incorporación de un contexto de base de datos

  1. En Visual Studio Code, cree una nueva carpeta en la carpeta BlazingPizza. Asígnele el nombre Data.

  2. Cree un nuevo archivo en la carpeta Data. Asígnelo el nombre PizzaStoreContext.cs.

  3. Escriba este código para la clase:

    using Microsoft.EntityFrameworkCore;
    
    namespace BlazingPizza.Data;
    
    public class PizzaStoreContext : DbContext
    {
        public PizzaStoreContext(DbContextOptions options) : base(options)
        {
        }
    
        public DbSet<PizzaSpecial> Specials { get; set; }
    }    
    

    Esta clase crea un contexto de base de datos que se puede usar para registrar un servicio de base de datos. El contexto también nos permite tener un controlador que accede a la base de datos.

  4. Guarde los cambios.

Incorporación de un controlador

  1. Cree una nueva carpeta en la carpeta BlazingPizza. Asígnele el nombre Controllers.

  2. Cree un nuevo archivo en la carpeta Controllers. Asígnelo el nombre SpecialsController.cs.

  3. Escriba este código para la clase:

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.EntityFrameworkCore;
    using BlazingPizza.Data;
    
    namespace BlazingPizza.Controllers;
    
    [Route("specials")]
    [ApiController]
    public class SpecialsController : Controller
    {
        private readonly PizzaStoreContext _db;
    
        public SpecialsController(PizzaStoreContext db)
        {
            _db = db;
        }
    
        [HttpGet]
        public async Task<ActionResult<List<PizzaSpecial>>> GetSpecials()
        {
            return (await _db.Specials.ToListAsync()).OrderByDescending(s => s.BasePrice).ToList();
        }
    }
    

    Esta clase crea un controlador que nos permitirá consultar en la base de datos las pizzas especiales y devolverlas como JSON en la dirección URL (http://localhost:5000/specials).

  4. Guarde los cambios.

Carga de datos en la base de datos

La aplicación comprueba si existe una base de datos SQLite y creará una con algunas pizzas creadas previamente.

  1. Cree un nuevo archivo en el directorio Data. Asígnelo el nombre SeedData.cs.

  2. Escriba este código para la clase:

    namespace BlazingPizza.Data;
    
    public static class SeedData
    {
        public static void Initialize(PizzaStoreContext db)
        {
            var specials = new PizzaSpecial[]
            {
                new PizzaSpecial()
                {
                    Name = "Basic Cheese Pizza",
                    Description = "It's cheesy and delicious. Why wouldn't you want one?",
                    BasePrice = 9.99m,
                    ImageUrl = "img/pizzas/cheese.jpg",
                },
                new PizzaSpecial()
                {
                    Id = 2,
                    Name = "The Baconatorizor",
                    Description = "It has EVERY kind of bacon",
                    BasePrice = 11.99m,
                    ImageUrl = "img/pizzas/bacon.jpg",
                },
                new PizzaSpecial()
                {
                    Id = 3,
                    Name = "Classic pepperoni",
                    Description = "It's the pizza you grew up with, but Blazing hot!",
                    BasePrice = 10.50m,
                    ImageUrl = "img/pizzas/pepperoni.jpg",
                },
                new PizzaSpecial()
                {
                    Id = 4,
                    Name = "Buffalo chicken",
                    Description = "Spicy chicken, hot sauce and bleu cheese, guaranteed to warm you up",
                    BasePrice = 12.75m,
                    ImageUrl = "img/pizzas/meaty.jpg",
                },
                new PizzaSpecial()
                {
                    Id = 5,
                    Name = "Mushroom Lovers",
                    Description = "It has mushrooms. Isn't that obvious?",
                    BasePrice = 11.00m,
                    ImageUrl = "img/pizzas/mushroom.jpg",
                },
                new PizzaSpecial()
                {
                    Id = 7,
                    Name = "Veggie Delight",
                    Description = "It's like salad, but on a pizza",
                    BasePrice = 11.50m,
                    ImageUrl = "img/pizzas/salad.jpg",
                },
                new PizzaSpecial()
                {
                    Id = 8,
                    Name = "Margherita",
                    Description = "Traditional Italian pizza with tomatoes and basil",
                    BasePrice = 9.99m,
                    ImageUrl = "img/pizzas/margherita.jpg",
                },
            };
            db.Specials.AddRange(specials);
            db.SaveChanges();
        }
    }
    

    La clase usa un contexto de base de datos pasado, crea algunos objetos PizzaSpecial en una matriz y, a continuación, los guarda.

  3. En el explorador de archivos, seleccione Program.cs.

  4. En la parte superior, agregue una referencia a un nuevo PizzaStoreContext:

    using BlazingPizza.Data;
    

    Esta instrucción permite que la aplicación use el nuevo servicio.

  5. Inserte este segmento encima del método app.Run();:

    ...
    // Initialize the database
    var scopeFactory = app.Services.GetRequiredService<IServiceScopeFactory>();
    using (var scope = scopeFactory.CreateScope())
    {
        var db = scope.ServiceProvider.GetRequiredService<PizzaStoreContext>();
        if (db.Database.EnsureCreated())
        {
            SeedData.Initialize(db);
        }
    }
    
    app.Run();
    

    Este cambio crea un ámbito de base de datos con PizzaStoreContext. Si no hay una base de datos ya creada, llama a la clase estática SeedData para crear una.

  6. Por el momento, la aplicación no funciona, ya que no se ha inicializado PizzaStoreContext. En la sección Add Services to the container superior del archivo Program.cs, agregue este código bajo los servicios actuales (las líneas que empiezan con builder.Services.):

      builder.Services.AddHttpClient();
      builder.Services.AddSqlite<PizzaStoreContext>("Data Source=pizza.db");
    
    

    Este código registra dos servicios. La primera instrucción AddHttpClient permite que la aplicación acceda a comandos HTTP. La aplicación usa HttpClient para obtener el JSON de los especiales de pizza. La segunda instrucción registra el nuevo elemento PizzaStoreContext y proporciona el nombre de archivo de la base de datos SQLite.

Uso de la base de datos para mostrar pizzas

Ahora podemos reemplazar la pizza codificada de forma rígida en la página Index.razor.

  1. En el explorador de archivos, seleccione Index.razor.

  2. Reemplace el método OnInitialized() existente con:

    protected override async Task OnInitializedAsync()
    {
        specials = await HttpClient.GetFromJsonAsync<List<PizzaSpecial>>(NavigationManager.BaseUri + "specials");
    }
    

    Nota

    Este código ha reemplazado OnInitialized() por OnInitializedAsync(). Las especiales se van a devolver ahora como JSON desde la aplicación de forma asincrónica.

  3. Hay algunos errores que debe corregir. Agregue estas instrucciones @inject en la directiva @page:

    @inject HttpClient HttpClient
    @inject NavigationManager NavigationManager
    
  4. Guarde todos los cambios y, a continuación, seleccione F5 o Ejecutar. A continuación, seleccione Iniciar depuración.

    Se produce un error en el entorno de ejecución al ejecutar la aplicación. JsonReader generó una excepción.

  5. Recuerde que la aplicación crea el JSON en (http://localhost:5000/specials). Vaya a esa dirección URL.

    La aplicación no sabe cómo enrutar esta solicitud. Obtendrá información sobre el enrutamiento en el módulo sobre enrutamiento de Blazor. Ahora vamos a corregir el error.

  6. Seleccione Mayús + F5 o Detener depuración.

  7. En el explorador de archivos, seleccione Program.cs.

  8. Alrededor del centro del archivo, después de las líneas que empiezan con app., agregue este punto de conexión:

    app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
    

    El código debe ser ahora:

    ...
    app.MapRazorPages();
    app.MapBlazorHub();
    app.MapFallbackToPage("/_Host");
    app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
    ...
    
  9. Seleccione F5 o Ejecutar. A continuación, seleccione Iniciar depuración.

    La aplicación debería funcionar, pero vamos a comprobar que el archivo JSON se crea correctamente.

  10. Vaya a (http://localhost:5000/specials) para ver:

    Screenshot showing the browser that shows JSON for pizzas.

    El JSON enumera las pizzas en orden descendente de precios, tal y como se especifica en el controlador de pizzas especiales.

    Screenshot showing even more blazing pizzas.