查看: 124|回复: 0

Blazor 实战 25:将 ASP.NET Core 后端添加到 Blazor ...

[复制链接]

4

主题

5

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 2023-1-19 13:26:09 | 显示全部楼层 |阅读模式
在本附录中,我将介绍将 ASP.NET Core 后端添加到现有 Blazor WebAssembly 应用程序所需的步骤。 后端将由 ASP.NET Core Web API、.NET 类库和 SQLite 数据库组成。 值得指出的是,如果您要开始一个新项目,那么会包含一个模板,其中包含这个确切的设置,减去数据库。 它称为 Blazor WebAssembly ASP.NET Core 托管模板(在第 2 章中介绍)。 因此,如果您是新手,我建议您使用它并避免执行本附录中介绍的手动步骤。
注意 我们将从第 4 章末尾的状态开始开发 Blazing Trails 应用程序。如果您正在按照本书的章节进行构建,则需要在完成第 5 章之前完成本附录。
A.1 添加 ASP.NET Core Web API
我们将首先添加 Web API 项目。 图 A.1 显示了我们解决方案的起点。



图 A.1 我们解决方案的起点。

它目前包含一个 Blazor WebAssembly 应用程序。
我们将添加一个名为 BlazingTrails.Api 的新 Web API 项目。 将新项目添加到现有解决方案时,我更喜欢使用 .NET CLI 从命令行执行此操作。 我发现它比在 Visual Studio 或其他 IDE 中单击菜单要快一些,但请使用最适合您的方法。
导航到包含解决方案文件的文件夹,然后执行以下命令:
dotnet new webapi -n BlazingTrails.Api通过此命令,我们要求 CLI 创建一个名为 BlazingTrails.Api 的新 Web API 项目。 一两秒钟后,将创建项目。 要将新项目添加到现有解决方案,我们然后运行此命令:
dotnet sln add BlazingTrails.Api\BlazingTrails.Api.csproj您应该会看到一条消息,说明该项目已添加到解决方案中。 此时,您可以在 Visual Studio 中打开解决方案,或者如果它已经打开则重新加载它。 您应该会在解决方案资源管理器中看到新的 API 项目(图 A.2)。



图 A.2 新的 API 项目现在是现有解决方案的一部分。

我们现在已经成功地将我们的 API 项目添加到解决方案中。 接下来,我们需要清除模板中包含的一些样板代码,然后将其配置为与我们的 Blazor 应用程序一起使用。 当我们完成时,BlazingTrails.Api 项目将成为解决方案的启动项目,并将服务于 Blazor WebAssembly 应用程序。
A.1.1 从新的 API 项目中删除样板文件
一个新的 ASP.NET Core Web API 带有一个名为 WeatherForecastController 的示例控制器和一个 WeatherForecast 类。 这些是初次学习构建 Web API 时可以使用的方便示例,但它们对我们没有任何用处。 因此,我们要做的第一件事是从项目的根目录中删除 WeatherForecast 类,并删除整个 Controllers 文件夹,以及其中的 WeatherForecastController。
新的 API 也安装了 Swagger。 Swagger 是一个用于记录 API 的出色工具,并且根据您对 API 项目的需求,您可能希望保留它; 但是,我们正在配置的 API 仅用于我们的 Blazor 应用程序,因此我们将删除它。 执行此操作分为三个步骤。
从项目 (Swashbuckle.AspNetCore) 中删除 Swagger NuGet 包。
从 Program.cs 中删除 Swagger 服务和中间件。
从 Properties 文件夹中的 launchSettings.json 文件中删除 launchUrl 属性。
完成这些步骤后,Swagger 的所有痕迹都将从项目中删除,我们可以专注于为我们的 Blazor 应用程序配置它。
A.1.2 配置接口
现在我们有一个干净的项目可以使用,我们可以开始根据我们的需要配置它。 首先,我们将引用一个 NuGet 包,它允许我们配置 API 来为 Blazor 应用程序提供服务。 在 csproj 文件中,添加以下包引用。 或者,可以使用 Visual Studio 中的 NuGet 包管理器 GUI 添加包:
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.0" />此包包含将使 API 项目能够为 Blazor WebAssembly 应用程序提供服务的中间件。 有了它,我们可以转到 Program.cs 文件进行一些修改。 将文件中的代码替换为以下清单中显示的代码。
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseWebAssemblyDebugging();     ❶
}

app.UseHttpsRedirection();

app.UseBlazorFrameworkFiles();         ❷
app.UseStaticFiles();                  ❸

app.UseRouting();

app.MapControllers();
app.MapFallbackToFile("index.html");   ❹

app.Run();❶ 这个中间件可以调试 Blazor WebAssembly 代码。
❷ 该中间件使 API 能够为 Blazor 应用程序提供服务。
❸ 此中间件使静态文件能够由 API 提供服务。
❹ 如果请求与控制器不匹配,则提供来自 Blazor 项目的 index.html 文件。
更改中需要注意的关键点是添加了 UseWebAssemblyDebugging 中间件。 一旦我们切换到使用 API 作为启动项目,这允许我们仍然调试我们的 Blazor WebAssembly 代码。
接下来,我们添加了 UseBlazorFrameworkFiles() 和 UseStaticFiles() 中间件。 这些一起允许 API 为 Blazor 应用程序文件提供服务。 毕竟,Blazor WebAssembly 应用程序一旦编译,就只是一组静态文件。
另一个需要注意的变化是添加了 MapFallbackToFile 端点。 这指示 API 将与其端点之一不匹配的任何请求路由到 Blazor 应用程序,以便它可以尝试处理它。
接下来我们将跳回 launchSettings.json 文件。 在这里,我们将添加几行,如下一个清单所示。
"profiles": {
  "IIS Express": {
    "commandName": "IISExpress",
    "launchBrowser": true,
    "inspectUri": "{wsProtocol}://{url.hostname}:
    ➥{url.port}/_framework/debug/ws-proxy?
    ➥browser={browserInspectUri}",                  ❶
    "environmentVariables": {                        ❶
      "ASPNETCORE_ENVIRONMENT": "Development"        ❶
    }                                                ❶
  },                                                 ❶
  "BlazingTrails.Api": {                             ❶
    "commandName": "Project",                        ❶
    "dotnetRunMessages": "true",                     ❶
    "launchBrowser": true,                           ❶
    "inspectUri": "{wsProtocol}://{url.hostname}:    ❶
    ➥{url.port}/_framework/debug/ws-proxy?          ❶
    ➥browser={browserInspectUri}",                  ❶
    "applicationUrl": "https://localhost:5001;http://localhost:5000",
    "environmentVariables": {
      "ASPNETCORE_ENVIRONMENT": "Development"
    }
  }❶ inspectUri 属性在 Visual Studio、Visual Studio for macOS 和 Visual Studio Code 中启用 Blazor WebAssembly 调试。
我们刚刚添加的行允许在 Visual Studio IDE 系列和文本编辑器中调试 Blazor WebAssembly。 目前,其他 IDE(例如 JetBrains Rider)不支持此功能。
inspectUri 使 Visual Studio 能够识别它正在运行 Blazor WebAssembly 应用程序。 然后它将尝试将脚本调试基础结构连接到 Blazor 的调试代理。 您可能会注意到 inspectUri 的值中有一些占位符; 这些将在调试会话期间由框架替换,不需要任何手动配置。
我们需要做的最后一项配置是将 BlazingTrails.Api 项目的项目引用添加到 BlazingTrails.Web 项目,如图 A.3 所示。



图 A.3 在 Visual Studio 中添加项目引用

在 API 项目下,右键单击 Dependencies 并从上下文菜单中选择 Add Project Reference。 这将打开参考管理器(图 A.4)。



图 A.4 引用管理器用于配置项目依赖项和其他引用,例如 DLL。

在 Reference Manager 中,选中 BlazingTrails.Client 项目旁边的框,然后单击 OK。 这将配置依赖项。
在我们运行解决方案之前,我们只需要将 API 项目设置为启动项目。 为此,右键单击 API 项目并从上下文菜单中选择设置为启动项目。 我们现在可以运行该应用程序。 如果一切顺利,我们应该会看到图 A.5 中所示的 Blazor 应用程序。



图 A.5 新 API 项目正在运行的 Blazor 应用程序

如果查看地址栏中的 URL,您会看到应用程序现在正在使用 API 项目的端口运行,而不是像以前那样使用客户端项目的端口。
A.2 添加 .NET 类库以在客户端和 API 之间共享代码
API 项目就位并配置为服务 Blazor 应用程序后,我们现在要添加一个 .NET 类库。 该库将用于在客户端和 API 项目之间共享代码——这是使用 Blazor 构建全栈 ASP.NET 应用程序的主要优势之一。
我们将使用 .NET CLI 来创建项目,就像我们使用 API 所做的那样。 从包含解决方案文件的文件夹开始,我们运行以下命令来生成新项目:
dotnet new classlib -n BlazingTrails.Shared这将生成一个名为 BlazingTrails.Shared 的新 .NET 类库,并将其文件放在同名目录中。 然后我们可以使用 sln add 命令将它添加到解决方案中:
dotnet sln add BlazingTrails.Shared\BlazingTrails.Shared.csproj此时,如果切换回 Visual Studio,您应该会在解决方案资源管理器中看到新项目(图 A.6)。 如果在运行 CLI 命令时解决方案处于打开状态,系统可能会提示您重新加载解决方案。



图 A.6 新的共享项目现在是现有解决方案的一部分。

要从客户端或 API 项目访问任何共享代码,我们需要在它们与共享项目之间设置项目引用。 我们以与之前相同的方式执行此操作。 从 API 项目开始,右键单击 Dependencies 节点并选择 Add Project Reference... 以打开 Reference Manager 对话框(图 A.7)。



图 A.7 通过 Visual Studio 中的引用管理器添加项目引用

在对话框中,选中 BlazingTrails.Shared 项目旁边的框,然后单击确定。 这将创建项目引用。 然后我们可以为客户项目重复这个过程。
我们要做的最后一步是删除作为项目一部分生成的 Class1.cs 文件。 这是一个空的类文件,不包含任何有价值的东西。 此时,您应该有一个不包含任何文件的空共享项目。
A.3 在 API 中设置 SQLite 数据库
在最后一部分中,我们将通过在 API 项目中配置 SQLite 数据库来完成后端设置。 我选择在本书中使用 SQLite,因为它是一个跨平台的可移植数据库。 您可以轻松地将它换成您选择的数据库,SQL Server、MySQL 或您喜欢的任何数据库。 为了与数据库交互,我们将使用 Entity Framework Core (http://mng.bz/aJKx),这是 Microsoft 的一种流行的对象关系映射器 (ORM)。
首先,我们将在 BlazingTrails.Api 项目中创建一个名为 Persistence 的新文件夹。 该文件夹将包含应用程序数据层所需的所有基础设施(图 A.8)。



图 A.8 API 项目中新的 Persistence 文件夹

在此文件夹中,我们再创建两个文件夹,Data 和 Entities。 最后,我们将在 Persistence 文件夹的根目录中添加一个名为 BlazingTrailsContext.cs 的新类。
我们还需要添加一些 NuGet 包。 以下包引用可以直接添加到 BlazingTrails.Api.csproj 文件中; 或者,可以通过 Visual Studio 中的 NuGet 包管理器 GUI 添加包:
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" PrivateAssets="all" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.0" />这些命令将安装 Entity Framework Core、EF Core SQLite 提供程序,以及一些工具来帮助我们稍后生成和管理迁移。
A.3.1 为系统配置初始实体
在我们可以对上下文进行任何操作之前,我们需要为我们的系统创建初始实体。 我们将创建两个名为 Trail 和 RouteInstruction 的实体。 这些只是 POCO(普通旧 CLR 对象),代表我们要为每种类型保存到数据库中的信息。
在 Entities 文件夹中,创建一个名为 Trail.cs 的类并添加以下代码。
public class Trail
{
    public int Id { get; set; }
    public string Name { get; set; } = default!;
    public string Description { get; set; } = default!;
    public string? Image { get; set; }
    public string Location { get; set; } = default!;
    public int TimeInMinutes { get; set; }
    public int Length { get; set; }

    public ICollection<RouteInstruction> Route
    ➥{ get; set; } = default!                    ❶
}❶ Route 是一个导航属性,有助于在 Trail 和 RouteInstructions 之间创建一对多关系。 这里我们说一个 Trail 有很多 RouteInstructions。
如您所见,该课程内容不多; 它只是定义构成路径的属性。 唯一值得指出的是底部的 Route 集合。 这是一个导航属性,将有助于在 Trail 和 RouteInstructions 之间创建一对多关系。 在 Trail 的情况下,我们说它可以有很多 RouteInstructions。
接下来让我们为 RouteInstruction 创建一个新类。 代码显示在以下清单中。
public class RouteInstruction
{
    public int Id { get; set; }
    public int TrailId { get; set; }
    public int Stage { get; set; }
    public string Description { get; set; } = default!;

    public Trail Trail { get; set; } = default!;     ❶
}❶ Trail 创建一对多关系的另一端。 这说明每个 RouteInstruction 可以有一个 Trail。
在 RouteInstruction 的代码中,我们可以看到正在创建的一对多关系的另一端。 它声明 RouteInstruction 只能有一个 Trail。
注意 此关系是按惯例创建的,但您也可以根据需要手动配置此关系。 这超出了本书的范围,但我建议查看 Microsoft Docs 站点 (http://mng.bz/XZYl),或获取 Jon Smith 撰写的 Entity Framework Core in Action (http:// mng.bz/yv97)以了解更多信息。
现在我们已经创建了初始实体,我们需要配置它们以与实体框架一起使用。 这个配置将允许我们指定一个属性在数据库中是否应该可以为空,或者它是否应该有字符限制,诸如此类。 这可以通过几种方式实现。
第一种是使用数据属性。 这些直接在实体上使用,每个属性都装饰有告诉实体框架应该如何在数据库表中配置该列的属性。 例如,要使 Trail 类的 Name 属性在数据库中不可为空,我们使用以下数据属性:
[Required]
public string Name { get; set; }您可以配置所有内容,从字段验证到它映射到的列的名称,甚至是表名。 如果您有兴趣使用此方法进行配置,我建议您阅读有关该主题的官方文档页面 (http://mng.bz/M5gE)。
配置实体的第二种方法是使用配置类。 我更喜欢这种方法,因为我不喜欢数据属性。 我还在我的很多专业项目中使用 DDD(领域驱动设计),我的领域实体不应该被持久性问题搞得一团糟。 使用此方法,所有配置都在一个单独的类中完成,并且实体不知道任何配置。
对于像 Blazing Trails 这样的小型项目,或者在不使用 DDD 的 CRUD(创建、读取、更新、删除)系统中,我倾向于将配置类保存在与实体相同的文件中。 这使得更新更容易,如果系统随着时间的推移变得更加复杂,我可以轻松地将配置类重构到它自己的文件中,并将实体移动到域项目中,几乎没有问题。
我们将首先配置 Trail 实体。 在 Trail.cs 文件中,我们将添加一个额外的类。 这将进入命名空间内部但在现有类之外。 代码显示在以下清单中。
public class TrailConfig : IEntityTypeConfiguration<Trail>       ❶
{
    public void Configure(EntityTypeBuilder<Trail> builder)
    {
        builder.Property(x => x.Name).IsRequired();              ❷
        builder.Property(x => x.Description).IsRequired();
        builder.Property(x => x.Location).IsRequired();
        builder.Property(x => x.TimeInMinutes).IsRequired();
        builder.Property(x => x.Length).IsRequired();
    }
}❶ IEntityTypeConfiguration<T> 允许我们为定义为 T 的实体指定配置。
❷ IEntityType-Configuration<T> 定义了Configure方法; 在这里,可以为模型上的每个属性指定规则。
要创建配置文件,我们需要继承 IEntityTypeConfiguration<T> 接口——T 是我们要配置的实体。 这个接口要求我们实现一个名为 Configure 的方法。 在 Configure 方法中,可以为实体的每个属性指定规则。 对于清单 A.5 中的代码,所有属性(IsFavourite 除外)都被标记为必需。
现在让我们为 RouteInstruction 实体做同样的事情。 和以前一样,我们将向现有文件添加一个新的配置类。 代码显示在以下清单中。
public class RouteInstructionConfig :
➥IEntityTypeConfiguration<RouteInstruction>                  ❶
{
    public void Configure(EntityTypeBuilder<RouteInstruction> builder)
    {
        builder.Property(x => x.TrailId).IsRequired();        ❷
        builder.Property(x => x.Stage).IsRequired();
        builder.Property(x => x.Description).IsRequired();
    }
}❶ IEntityTypeConfiguration<T> 允许我们为定义为 T 的实体指定配置。
❷ IEntityTypeConfiguration<T> 定义了Configure方法; 在这里,可以为模型上的每个属性指定规则。
与 Trail 配置一样,我们正在实现 IEntityTypeConfiguration<T> 接口。 然后在 Configure 方法中指定我们需要的规则。 再一次,我们只是根据需要设置一些属性。
A.3.2 设置数据库上下文
设置和配置实体后,我们可以将注意力转移到数据库上下文 BlazingTrailsContext。 数据库上下文是存储库模式和工作单元模式的组合。 在其中,我们基本上使用类型为 DbSet<T> 的属性来定义实体的集合。 然后我们可以将它注入我们的应用程序并使用它来访问和修改数据库中的数据。 以下清单显示了 BlazingTrailsContext 类的更新代码。
public class BlazingTrailsContext : DbContext              ❶
{
    public DbSet<Trail> Trails => Set<Trail>();            ❷
    public DbSet<RouteInstruction> RouteInstructions =>    ❷
      Set<RouteInstruction>();                             ❷

    public BlazingTrailsContext(DbContextOptions<BlazingTrailsContext>
    ➥options) : base(options) { }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.ApplyConfiguration(                   ❸
        ➥new TrailConfig());                              ❸
        modelBuilder.ApplyConfiguration(                   ❸
        ➥new RouteInstructionConfig());                   ❸
    }
}❶ DbContext 类提供上下文的所有基本功能。 所有数据库上下文都必须继承自此类。
❷ 每个实体都表示为类型为 DbSet<T> 的集合。 这些本质上是存储库。
❸ 通过重写 OnModelCreating 方法,我们可以挂接上一节创建的实体配置类。
首先,我们的上下文必须继承自 DbContext 类。 此类提供与数据库交互的所有管道。 接下来,我们需要定义实体的集合。 这些本质上是存储库,为我们提供了一种与数据库中包含实体数据的表进行交互的方式。
这个类还有一个需要注意的地方就是我们在上一节中创建的实体配置的应用。 这是通过覆盖 OnModelCreating 方法来完成的。 在此方法中,我们使用 ModelBuilder 对象应用每个配置。
A.3.3 连接字符串和服务配置
配置的最后一步是在 appsettings.json 文件中添加一个连接字符串,并将需要的服务添加到服务容器中。 在 appsettings.json 文件中,在最后一个右括号内添加以下内容:
"ConnectionStrings": {
  "BlazingTrailsContext": "DataSource=Persistence/Data/blazingtrails.db"
}实体框架将使用它在尝试保存或检索数据时定位数据库。 首次创建数据库时也会使用它来了解在文件系统中的何处创建它。
设置连接字符串后,我们只需要在 Program.cs 中的服务容器中注册实体框架服务。 为此,我们需要在 builder.Services 属性上调用 AddDbContext<T>() 方法,如以下代码所示:
builder.Services.AddDbContext<BlazingTrailsContext>(options => options.UseSqlite(builder.Configuration.GetConnectionString("BlazingTrailsContext")));
builder.Services.AddControllers();
此方法要求我们传递我们正在注册的上下文类型,并指定我们正在使用的数据库类型,以及我们将用来连接它的连接字符串。 此时,一切都已配置,我们已准备好生成我们的第一个迁移并创建初始数据库。
A.3.4 创建第一个迁移并创建数据库
配置完成后,我们现在可以为我们的应用程序创建初始迁移。 迁移包含两种方法,称为 Up 和 Down。 Up 方法包含基于新更改的所需数据库状态。 Down 方法包含有关如何在我们需要还原迁移时反转 Up 方法的说明。
要创建迁移,我们可以使用基于命令行的 Entity Framework Core 工具 (https://docs.microsoft.com/en-us/ef/core/cli/dotnet),或者我们可以使用 程序包管理器控制台,如果在 Windows 上使用 Visual Studio。 因为我在 Visual Studio 上,所以我将使用包管理器。 如果您当前没有打开此窗口,则可以通过转到主菜单 > 查看 > 其他窗口 > 程序包管理器控制台来打开它。
要创建迁移,我将在包管理器控制台中使用以下命令,确保默认项目设置为 BlazingTrails.Api:
Add-Migration InitialEntities -o Persistence/Data/Migrations在指定命令名称 Add-Migration 之后,我们为迁移命名。 我建议使用驼峰式大小写来提高可读性。 然后我们将迁移的输出位置指定为 Persistence > Data 位置中名为 Migrations 的文件夹。 运行该命令将触发应用程序的构建,几秒钟后,应该会显示迁移。 图 A.9 显示了在 API 项目中创建的新文件和文件夹。



图 A.9 新的 Migrations 文件夹包含初始迁移。

运行 Add-Migration 命令后,将创建一个新的 Migrations 文件夹,其中包含新的迁移和实体框架自动生成的模型快照。
现在迁移已经到位,我们可以创建初始数据库。 为此,我们在包管理器控制台中运行 Update-Database 命令。 这将获取我们刚刚创建的迁移中的代码,并生成一个包含两个表 Trails 和 RouteInstructions 的新数据库。 如果一切按预期进行,那么您应该会看到数据库出现在解决方案资源管理器中,如图 A.10 所示。



图 A.10 Visual Studio 解决方案资源管理器中显示的新 SQLite 数据库

至此,Blazing Trails 的后台已经准备就绪! 我们有一个新的 API,我们已经配置了一个新的数据库,我们可以在其中存储未来的追踪数据。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表