从 Blazor 组件访问数据

已完成

具备吸引力的网站需要显示可能随时变化的动态内容。 从数据库或 Web 服务等动态源获取数据是 Web 开发中的一项基本技术。

假设你就职于一家披萨派送公司,负责处理其更新的面向客户的网站。 你有一系列布局和设计为 Blazor 组件的网页。 现在,你想要使用从数据库中获取的有关披萨、配料和订单的信息来填充这些页面。

在本单元中,你将学习如何访问数据,并在 HTML 标记中呈现这些数据以显示给用户。

创建已注册的数据服务

如果要创建向用户显示不断变化的信息的动态网站,必须编写代码才能从某处获取这些数据。 例如,假设你有一个存储公司销售的所有披萨的数据库。 由于披萨始终在变化,因此将它们硬编码到网站 HTML 中并非明智之举。 相反,可以使用 C# 代码和 Blazor 来查询数据库,然后将详细信息格式化为 HTML,以便用户可以选择自己喜欢的内容。

在 Blazor Server 应用中,可以创建已注册的服务来表示数据源,然后从中获取数据。

备注

可在 Blazor 应用中使用的数据源包括关系数据库、NoSQL 数据库、Web 服务、各种 Azure 服务以及许多其他系统。 可以使用实体框架、HTTP 客户端和 ODBC 等 .NET 技术来查询这些源。 这些技术不在本模块的涵盖范围之内。 在本模块中,你将了解如何格式化和使用从这些源通过这些技术获取的数据。

通过编写定义其属性的类,开始创建已注册的服务。 下面是可能编写的用来表示披萨的示例:

namespace BlazingPizza.Data;

public class Pizza
{
    public int PizzaId { get; set; }
    
    public string Name { get; set; }
    
    public string Description { get; set; }
    
    public decimal Price { get; set; }
    
    public bool Vegetarian { get; set; }
    
    public bool Vegan { get; set; }
}

类定义披萨的属性和数据类型。 必须确保这些属性与数据源中的披萨架构匹配。 在项目的 Data 文件夹中创建此类并使用名为 Data 的成员命名空间是有意义的。 如果你愿意,也可以选择其他文件夹和命名空间。

接下来,将定义服务:

namespace BlazingPizza.Data;

public class PizzaService
{
    public Task<Pizza[]> GetPizzasAsync()
    {
    // Call your data access technology here
    }
}

请注意,该服务使用异步调用来访问数据并返回 Pizza 对象的集合。 数据源可能远离运行 Blazor 代码的服务器。 在这种情况下,请使用异步调用。 如果数据源响应缓慢,其他代码可以在你等待响应时继续运行。

还可以通过向 Program.cs 文件的 Add Services to the container 部分添加一个行来注册服务:

...
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
// Register the pizzas service
builder.Services.AddSingleton<PizzaService>();
...

使用服务获取数据

现在使用定义的服务,具体操作是在 Blazor 组件中调用该服务并获取数据。 假设你有以下组件代码,并且想要在其中显示披萨:

@page "/pizzas"

<h1>Choose your pizza</h1>

<p>We have all these delicious recipes:</p>

注入服务

必须先使用依赖项注入来添加服务,然后才能从组件调用该服务。 通过在 @page 指令后添加以下代码来注入服务:

@using BlazingPizza.Data
@inject PizzaService PizzaSvc

通常,组件和服务将位于不同的命名空间成员中,因此你必须添加 @using 指令。 该指令的工作方式与 C# 代码文件顶部的 using 语句相同。 @inject 指令将服务添加到当前组件并启动其实例。 在指令中,指定服务类的名称。 后跟要用于此组件中的服务实例的名称。

替代 OnInitializedAsync 方法

OnInitializedAsync 方法是调用服务和获取数据的好位置。 当组件初始化完成且收到初始参数时,在呈现页面并显示给用户之前,将触发此事件。 事件是在 Blazor 组件的基类上定义的。 可以在代码块中替代它,如本例所示:

protected override async Task OnInitializedAsync()
{
    \\ Call the service here
}

调用服务以获取数据

当你调用服务时,请使用 await 关键字,因为调用是异步进行的:

private Pizza[] todaysPizzas;

protected override async Task OnInitializedAsync()
{
    todaysPizzas = await PizzaSvc.GetPizzasAsync();
}

向用户显示数据

从服务获取某些数据后,想要将其显示给用户。 在披萨示例中,我们希望服务返回可供用户选择的披萨列表。 Blazor 包含一组丰富的指令,你可以使用这些指令将这些数据插入到用户看到的页面中。

检查数据

首先,确定加载披萨之前页面所显示的内容。 可以通过检查 todaysPizzas 集合是否为 null 来实现这一点。 若要在 Blazor 组件中运行条件呈现代码,请使用 @if 指令:

@if (todaysPizzas == null)
{
    <p>We're finding out what pizzas are available today...</p>
}
else
{
    <!-- This markup will be rendered once the pizzas are loaded -->
}

仅当 C# 表达式返回 @if 时,true 指令才会在其第一个代码块中呈现标记。 还可以使用 else if 代码块来运行其他测试,并在它们为 true 时呈现标记。 最后,如果上述条件均未返回 true,则可以指定 else 代码块来呈现代码。 通过在 @if 代码块中检查 null,可以确保在从服务获取数据之前 Blazor 不会尝试显示披萨详细信息。

备注

Blazor 还包括 @switch 指令,该指令用于基于可能返回多个值的测试呈现标记。 @switch 指令的工作方式类似于 C# switch 语句。

呈现对象的集合

如果 Blazor 执行上述代码中的 else 语句,表明你知道已从服务中获取了部分披萨。 下一个任务就是向用户显示这些披萨。 让我们看看如何在简单的 HTML 表中显示这些数据。

在编写此页的代码时,我们不知道可用的披萨有多少。 我们可以使用 @foreach 指令来循环遍历 todaysPizzas 集合中的所有对象,并为每个对象呈现一行:

<table>
 <thead>
  <tr>
   <th>Pizza Name</th>
   <th>Description</th>
   <th>Vegetarian?</th>
   <th>Vegan?</th>
   <th>Price</th>
  </tr>
 </thead>
 <tbody>
  @foreach (var pizza in todaysPizzas)
  {
   <tr>
    <td>@pizza.Name</td>
    <td>@pizza.Description</td>
    <td>@pizza.Vegetarian</td>
    <td>@pizza.Vegan</td>
    <td>@pizza.Price</td>
   </tr>
  }
 </tbody>
</table>

Screenshot showing how the list of pizzas appears on a Blazor component.

当然,与此示例中显示的纯表相比,你可能希望显示更丰富的披萨内容。 你可能需要设置价格和其他值的格式。 与图形设计师合作,开发更具吸引力的 UI。 例如,包括每个披萨的图片。

备注

Blazor 还包含其他循环指令,例如 @for@while@do while。 这些指令返回重复的标记块。 它们的工作方式与等效的 C# forwhiledo...while 循环类似。

在下一单元中,你将注册自己的数据服务!

知识检查

1.

哪个 Blazor 事件处理程序是提取数据的好位置?

2.

应使用哪个 Blazor 指令来处理 Blazor 页上的数据访问服务?