处理数据

小窍门

此内容摘自电子书《面向 Azure ASP.NET Web Forms 开发人员的 Blazor》,可在 .NET 文档上获取,也可作为免费可下载的 PDF 脱机阅读。

《面向 ASP NET Web Forms 开发人员的 Blazor》电子书封面缩略图。

数据访问是 ASP.NET Web 窗体应用的主干。 如果要构建 Web 窗体,会对该数据造成什么影响? 使用 Web 窗体时,可以使用多种数据访问技术与数据库交互:

  • 数据源
  • ADO.NET
  • 实体框架

数据源是可在 Web 窗体页上放置并像配置其他控件一样配置的控件。 Visual Studio 提供了一组友好的对话框,用于配置控件并将其绑定到 Web 窗体页面。 首次发布 Web 窗体时,享受“低代码”或“无代码”方法的开发人员首选此方法。

数据源

ADO.NET 是与数据库交互的低级别方法。 应用可以使用用于交互的命令、数据表和数据集创建与数据库的连接。 然后,结果可以绑定到屏幕上的字段,而无需太多代码。 此方法的缺点是,每个 ADO.NET 对象(ConnectionCommandDataTable)集都绑定到数据库供应商提供的库。 使用这些组件使代码变得僵化且难以迁移到其他数据库。

实体框架

Entity Framework (EF)是由 .NET Foundation 维护的开源对象关系映射框架。 EF 最初随 .NET Framework 一起发布,可用于生成数据库连接、存储架构和交互的代码。 使用此抽象,你可以专注于应用的业务规则,并允许数据库由受信任的数据库管理员管理。 在 .NET 中,可以使用名为 EF Core 的 EF 的更新版本。 EF Core 通过一系列可通过命令行工具使用的命令,帮助生成和维护您的代码与数据库之间的交互。 让我们看看一些示例,帮助你更好地使用数据库。

EF Code First

开始生成数据库交互的快速方法是从要使用的类对象开始。 EF 提供了一个工具,可帮助为类生成适当的数据库代码。 此方法称为“Code First”开发。 对于我们要存储在关系数据库中(如 Microsoft SQL Server)的示例店面应用,请考虑以下 Product 类。

public class Product
{
    public int Id { get; set; }

    [Required]
    public string Name { get; set; }

    [MaxLength(4000)]
    public string Description { get; set; }

    [Range(0, 99999,99)]
    [DataType(DataType.Currency)]
    public decimal Price { get; set; }
}

产品有一个主键和三个将在数据库中创建的其他字段:

  • EF 将按照约定将 Id 属性标识为主键。
  • Name 将存储在为文本存储配置的列中。 修饰符 [Required] 的作用是给属性添加一个 not null 约束,以帮助强化该属性的声明行为。
  • Description 将存储在为文本存储配置的列中,并且属性所指示 [MaxLength] 的最大长度配置为 4000 个字符。 数据库架构将配置一个名为MaxLength的列,并使用数据类型varchar(4000)
  • Price 属性将存储为货币。 该 [Range] 属性将生成适当的约束,以防止在声明的最小值和最大值之外存储数据。

我们需要将此类 Product 添加到一个定义与数据库连接和转换操作的数据库上下文类中。

public class MyDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
}

MyDbContext 类提供一个属性,用于定义类 Product 的访问和转换。 应用程序通过在Startup类的ConfigureServices方法中使用以下条目配置该类,以便与数据库交互(或在Program.cs中使用builder.Services属性代替services的恰当位置)。

services.AddDbContext<MyDbContext>(options =>
    options.UseSqlServer("MY DATABASE CONNECTION STRING"));

上述代码将使用指定的连接字符串连接到 SQL Server 数据库。 可以将连接字符串放置在 appsettings.json 文件、环境变量或其他配置存储位置中,并相应地替换此嵌入字符串。

然后,可以使用以下命令生成适合此类的数据库表:

dotnet ef migrations add 'Create Product table'
dotnet ef database update

第一个命令将对数据库架构所做的更改定义为名为 Create Product table 的新 EF 迁移。 迁移定义如何应用和删除新数据库更改。

应用后,数据库中有一个简单的 Product 表和一些添加到项目中的新类,以帮助管理数据库架构。 默认情况下,可以在名为 Migrations 的新文件夹中找到这些生成的类。 对类进行更改 Product 或添加想要与数据库交互的更多相关类时,需要使用新的迁移名称再次运行命令行命令。 此命令将生成另一组迁移类来更新数据库架构。

EF Database First

对于现有数据库,可以使用 .NET 命令行工具为 EF Core 生成类。 若要生成类基架,请使用以下命令的不同变体。

dotnet ef dbcontext scaffold "CONNECTION STRING" Microsoft.EntityFrameworkCore.SqlServer -c MyDbContext -t Product -t Customer

上述命令使用指定的连接字符串和 Microsoft.EntityFrameworkCore.SqlServer 提供程序连接到数据库。 连接后,将创建一个名为 MyDbContext 的数据库上下文类。 此外,将为通过Product选项指定的Customer-t表格创建支持类。 此命令有许多配置选项可用于生成适合数据库的类层次结构。 有关完整参考,请参阅 命令的文档

有关 EF Core 的详细信息,请参阅 Microsoft Docs 站点。

与 Web 服务交互

首次发布 ASP.NET 时,SOAP 服务是 Web 服务器和客户端交换数据的首选方法。 自那以后发生了很大的变化,与服务的首选交互已转向直接 HTTP 客户端交互。 使用 ASP.NET Core 和 Blazor,您可以在 HttpClient 中或在 类的 Startup 方法中注册 ConfigureServices 的配置。 如果需要与 HTTP 终结点交互,请使用该配置。 请考虑以下配置代码:

// in Program.cs
builder.Services.AddHttpClient("github", client =>
{
    client.BaseAddress = new Uri("http://api.github.com/");
    // GitHub API versioning
    client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
    // GitHub requires a user-agent
    client.DefaultRequestHeaders.Add("User-Agent", "BlazorWebForms-Sample");
});

每当需要从 GitHub 访问数据时,请创建一个名称为 的 github客户端。 客户端配置了基址,并相应地设置请求标头。 使用IHttpClientFactory指令或Blazor属性将@inject注入到[Inject]组件中。 使用以下语法创建命名客户端并与服务交互:

@inject IHttpClientFactory factory

...

@code {
    protected override async Task OnInitializedAsync()
    {
        var client = factory.CreateClient("github");
        var response = await client.GetAsync("repos/dotnet/docs/issues");
        response.EnsureStatusCode();
        var content = await response.Content.ReadAsStringAsync();
    }
}

此方法返回描述 dotnet/docs GitHub 存储库中问题的集合的字符串。 它以 JSON 格式返回内容,并反序列化为相应的 GitHub 问题对象。 可以通过多种方式来配置 HttpClientFactory 以交付预配置的 HttpClient 对象。 尝试为处理的各种 Web 服务配置具有不同名称和终结点的多个 HttpClient 实例。 借助此方法,可以更容易地在每个页面上处理与这些服务的交互。 有关详细信息,请参阅 使用 IHttpClientFactory 发出 HTTP 请求