练习 - 从 Blazor 组件访问数据

已完成

需要将应用中当前硬编码的披萨替换为数据库。 你可以使用 Microsoft 实体框架向数据源添加连接。 在本应用中,我们将使用 SQLite 数据库来存储披萨。

在此练习中,你将添加包以支持数据库功能、将类连接到后端数据库,并添加帮助程序类以预加载公司披萨数据。

添加包以支持数据库访问

  1. 如果应用仍在运行,请停止它。

  2. 在 Visual Studio Code 中,选择“终端”>“新终端”。

  3. 在新终端中,将位置设置为 BlazingPizza 目录。

    cd BlazingPizza
    
  4. 运行以下命令以添加 Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.Sqlite 和 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
    

    以下命令向 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>
    

添加数据库上下文

  1. 在 Visual Studio Code 中,在 BlazingPizza 文件夹中创建新文件夹。 将其命名为 Data。

  2. 在 Data 文件夹中创建新文件。 将其命名为 PizzaStoreContext.cs。

  3. 为类输入以下代码:

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

    此类创建可用于注册数据库服务的数据库上下文。 该上下文还让我们拥有将访问数据库的控制器。

  4. 保存所做更改。

添加控制器

  1. 在 BlazingPizza 文件夹中创建新文件夹。 将其命名为 Controllers。

  2. 在 Controllers 文件夹中创建新文件。 将其命名为 SpecialsController.cs。

  3. 为类输入以下代码:

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

    此类创建一个控制器,让我们能够在数据库中查询披萨特价商品,并在 (http://localhost:5000/specials) URL 以 JSON 的形式返回这些内容。

  4. 保存所做更改。

将数据加载到数据库中

应用将检查是否存在现有的 SQLite 数据库,并使用一些预先制作的披萨创建一个数据库。

  1. 在 Data 目录中创建新文件。 将其命名为 SeedData.cs。

  2. 为类输入以下代码:

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

    类使用传递的数据库上下文,在数组中创建一些 PizzaSpecial 对象,然后保存它们。

  3. 在文件资源管理器中,选择“Program.cs”。

  4. 在顶部,添加对新 PizzaStoreContext 的引用:

    using BlazingPizza.Data;
    

    此语句允许应用使用新服务。

  5. 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();
    

    此更改使用 PizzaStoreContext 创建数据库作用域。 如果尚未创建数据库,它将调用 SeedData 静态类来创建一个数据库。

  6. 此时,应用无法正常工作,因为我们尚未初始化 PizzaStoreContext。 在 Program.cs 文件上方的 Add Services to the container 部分中,将以下代码添加到当前服务下(以 builder.Services. 开头的行):

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

    此代码注册两项服务。 第一条 AddHttpClient 语句允许应用访问 HTTP 命令。 应用使用 HttpClient 获取披萨特价商品的 JSON。 第二条语句注册新的 PizzaStoreContext,并提供 SQLite 数据库的文件名。

使用数据库显示披萨

现在可以替换 Index.razor 页中的硬编码的披萨。

  1. 在文件资源管理器中,选择“Index.razor”。

  2. 使用以下内容替换现有 OnInitialized() 方法:

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

    注意

    此代码已将 OnInitialized() 替换为 OnInitializedAsync()。 现在,特价商品将从应用异步返回为 JSON。

  3. 需要修复一些错误。 在 @page 指令下添加以下 @inject 语句:

    @inject HttpClient HttpClient
    @inject NavigationManager NavigationManager
    
  4. 保存所有更改,然后选择 F5 或选择“运行”。 然后选择“开始调试”。

    当你运行应用时出现运行时错误。 JsonReader 引发了一个异常。

  5. 请记住,应用应在 (http://localhost:5000/specials) 创建 JSON。 请转到该 URL。

    应用不知道如何路由此请求。 你将在此模块的“Blazor 路由”中了解路由。 现在,让我们修复错误。

  6. 选择 Shift + F5,或选择“停止调试”。

  7. 在文件资源管理器中,选择“Program.cs”。

  8. 在文件中间,在以 app. 开头的行之后,添加以下终结点:

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

    代码现在应为:

    ...
    app.MapRazorPages();
    app.MapBlazorHub();
    app.MapFallbackToPage("/_Host");
    app.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
    ...
    
  9. 选择 F5 或“运行”。 然后选择“开始调试”。

    应用现在应能够正常工作,但让我们检查一下是否正确创建了 JSON。

  10. 请转到 (http://localhost:5000/specials) 以查看:

    Screenshot showing the browser that shows JSON for pizzas.

    JSON 按照特价披萨控制器中指定的价格以降序列出了披萨。

    Screenshot showing even more blazing pizzas.