ASP.NET Core 和 Blazor 在 Mac 上的 Docker 应用






4.79/5 (5投票s)
一步一步演示如何在 macOS 上构建和 Docker 化 ASP.NET Core 和 Blazor 应用
引言
我一直在探索 .NET Core,从它还是 vNext 到 ASP.NET 5,直到它正式成为 .NET Core / ASP.NET Core 1.0 版本。我非常高兴和惊讶微软实现了重构 .NET 并使其跨平台、无处不在。对我来说,我认为这是微软有史以来最伟大的里程碑之一,因为它向全新的开发者和设计师群体开放了 .NET。
技术在不断发展,作为开发者,我们需要跟上最新的或至少是当下流行的技术。作为初学者,你可能会发现难以跟上最新技术,因为它会让你更加困惑,不知道应该使用哪些技术组合,以及从哪里开始。我们知道市面上有大量的资源可以作为参考学习,但你仍然很难将这些知识点联系起来。有时候,你可能会失去学习的兴趣而放弃。如果你感到困惑,不知道如何开始构建 ASP.NET Core 和 Blazor 应用并在 Docker 中运行它们,那么这篇文章就是为你准备的。
背景
过去几年我一直在研究“微服务”的概念,考虑到 .NET Core 是开源的、跨平台的、无处不在的,我很好奇在新的环境中运行它,以感受它的强大。然后,我开始 在 Mac 上探索 .NET Core 并在 Docker 中运行,当 1.0 版本发布后,一年后,我开始尝试 在 Mac 环境中 Docker 化 ASP.NET Core 2.0。这是一个有趣的实验,但同时也很困难,因为我习惯于 Windows 环境。在 Mac 环境中构建应用有点不同,因为我们要处理新的命令、工具和文件结构。换句话说,我对 Mac 环境不太熟悉,将所有东西组合起来非常麻烦。
今年早些时候,微软宣布了 ASP.NET 团队的一个新实验性项目,称为 Blazor。Blazor 是一个基于 C#、Razor 和 HTML 的实验性 Web UI SPA 框架,它通过 WebAssembly
在浏览器中运行,无需 JavaScript。是的,你没听错——无需 JavaScript!也就是说,Blazor 仍处于实验阶段,在正式发布之前,我们无法保证任何事情。
我个人觉得这个框架非常有趣。我认为 Blazor 将会非常受欢迎,因为我认为 WebAssembly
实际上将取代 JavaScript。当然,JavaScript 及其框架不会消失,但为什么要在教一个新程序员 JavaScript 时,不教他们 C#、Python 等,让他们在更简单的工具和更高效的环境中工作呢?
尝试 Blazor 一直在我的待办事项列表中,但由于工作优先级和忙碌,我一直没有时间去做。幸运的是,这周我找到了一些时间,尝试了 Blazor。我的第一次尝试是在 Windows 上运行它,但我还想在 Mac 上测试 Blazor 并在 Docker 中运行它。
我花了好几天时间才把所有东西都弄明白,弄清楚如何让它们工作。通过大量的研究、一些耐心和冥思苦想,我终于把所有东西都联系起来了。所以,在这里,我试图与那些也对此感兴趣的人分享这份乐趣和经验。
本文面向读者?
虽然网上有很多关于如何在 Docker 中构建和运行 .NET Core 应用的资源,但我发现关于在 Mac 上进行此操作的资源非常有限。本文将引导你构建第一个 Blazor 应用,该应用包含 ASP.NET Core Web API、Entity Framework Core、SQL Server,并在 Docker 容器中运行。
本文面向希望在 Mac 环境中进入 ASP.NET Core、Blazor 和 Docker 领域,并通过实际示例动手实践的 .NET 初学者到中级开发者。
我写这篇文章的方式是,通过提供详细的代码解释和分步过程,使其易于遵循和理解。当你阅读并完成本文的指导后,你将学会构建整个应用程序所使用的各项技术的基本概念和原理,以及它们是如何相互连接的。
你将学到什么
这是你可以在本文中学到的内容
- 我们想要实现的目标
- 设置开发环境
- 配置用于 Linux 的 SQL Server 数据库
- 使用 Valentina Studio 管理数据库
- 创建 ASP.NET Core Web API 应用程序
- Docker 化 Web API 应用程序
- 创建你的第一个 Blazor 应用程序
- 将所有应用程序从 Docker 容器连接起来
设置开发环境
让我们开始在 MAC 环境中安装构建应用程序所需的工具和 SDK。如果你已经安装了下面列表中的工具,那么你可以跳过此步骤,但请确保你已将它们更新到最新版本。
必备组件
macOS 10.12 “Sierra”及更高版本
你需要至少拥有 MAC 机器上的 macOS 10.12 “Sierra”版本,因为我们将使用 .NET Core 2.1 作为构建应用程序的目标框架。
Visual Studio Community
Community 版本是免费且功能齐全的解决方案,它使开发人员能够为 Android、iOS、macOS、云和 Web 构建应用程序。我们将使用此编辑器来使用 C# 代码构建 Blazor 和 ASP.NET Core Web API 应用。有关 Visual Studio Community 版本功能的更多信息,请参阅 此链接。
.NET Core (可选)
.NET Core 为我们提供了 dotnet 命令行工具,使我们可以在没有 IDE 的情况下构建和运行 .NET Core 应用。我将其标记为可选,因为 .NET Core SDK 应该包含在安装 Visual Studio for Mac 时。有关 .NET Core 的更多信息,请参阅 此链接。
Docker Engine
Docker 提供了简单的工具和通用的打包方法,将所有应用程序依赖项打包到一个容器中。Docker Engine 使容器化应用程序能够跨任何基础架构一致地运行。我们将看到如何使用 Docker 在容器中部署和运行 .NET Core 应用。有关 Docker Engine 的更多信息,请参阅 此链接。
Valentina Studio
据我所知,在撰写本文时,Microsoft SSMS 团队没有计划发布 SQL Server Management Studio 的跨平台版本。因此,如果你计划在 Mac 上使用 SQL Server 数据库并想要一个工具(GUI)来管理你的数据库,那么你可能需要使用第三方工具,如 Valentina Studio、SQLPro、Navicat、TablePlus 等。
本次演示,我将使用 Valentina Studio,因为它免费,并且不需要你在 Mac 上运行 Windows 虚拟机 (VM)。此外,它还支持 PostgreSQL、MySQL 和 SQLite 等几种数据库引擎。
下载源
- 适用于 MAC 的 .NET Core(截至撰写本文时,最新版本为 2.1.302)
- 适用于 MAC 的 Visual Studio Community 2017(截至撰写本文时,最新版本为 v7.6.7)
- 适用于 MAC 的 Docker Community Edition(截至撰写本文时,最新版本为 18.06.1_ce)
- Valentina Studio(截至撰写本文时,最新版本为 8.6 (64-bit))
引用请确保在继续操作之前下载并安装先决条件。
五人一球
从先决条件部分可以看出,我们将使用各种技术来构建整个应用程序以实现目标。此时,你应该已经在你的机器上安装了所需的框架。
我们的主要目标是使用尖端技术构建一个简单的数据驱动的 Web 应用程序:Blazor、ASP.NET Core Web API、Entity Framework Core、SQL Server 和 Docker。
在我们讨论每种技术如何连接在一起的高层应用程序流程之前,让我们先简单地概述一下它们。
ASP.NET Core Web API
ASP.NET Core Web API 是一个可扩展的框架,用于构建基于 HTTP 的服务,这些服务可以在不同平台上的不同应用程序中访问。它的工作方式与 ASP.NET Core MVC Web 应用程序大致相同,只是它以数据作为响应而不是 HTML 视图。它类似于 Web 服务或 WCF 服务,但例外是它仅支持 HTTP 协议。以下是摘自官方文档的定义。
引用ASP.NET Web API 是一个框架,可以轻松构建能够触及广泛客户端(包括浏览器和移动设备)的 HTTP 服务。ASP.NET Web API 是在 .NET Framework 上构建 RESTful 应用程序的理想平台。
Entity Framework Core
Entity Framework (EF) Core 是流行的 Entity Framework 数据访问技术的轻量级、可扩展且跨平台的版本。EF Core 是一个对象关系映射器 (O/RM),它使 .NET 开发人员能够使用 .NET 对象与数据库进行交互。它消除了开发人员通常需要编写的大部分数据访问代码。根据官方文档
引用作为一个 O/RM,EF 减少了关系世界和面向对象世界之间的阻抗不匹配,使开发人员能够编写与关系数据库中存储的数据交互的应用程序,使用表示应用程序域的强类型 .NET 对象,并消除了他们通常需要编写的大部分数据访问“管道”代码。
Blazor
Blazor 是一个基于 .NET Core 的单页 Web 应用 (SPA) 框架,它使用 WebAssembly 在浏览器中运行。有关 Blazor 的更多信息,请参阅 此链接。
引用警告!Blazor 是一个不受支持的实验性 Web 框架,目前不应用于生产工作负载。
SQL Server
Microsoft SQL Server 是由 Microsoft 开发的关系数据库管理系统。作为数据库服务器,它是一种软件产品,其主要功能是根据其他软件应用程序(桌面、服务、移动或 Web)的请求来存储和检索数据——这些应用程序可能运行在同一台计算机上,也可能运行在网络或互联网上的另一台计算机上。
Docker
Docker 提供容器软件,非常适合希望开始和试验基于容器的应用程序的开发人员和团队。
应用程序流程
上面的图表显示了我们在本文中想要实现的目标。我们基本上需要三个主要的 Docker 容器:一个用于 UI 的 Blazor 应用,一个用于提供 JSON 数据的 Web API,以及一个用于存储实际数据的 SQL Server。
让我们看看如何实现这一目标。我们先从设置数据库开始。
配置数据库
为了处理真实数据,我们需要一个持久化存储,称为“数据库”。在 Windows 环境中,设置新数据库非常简单直接。然而,在 macOS 的环境下,情况有所不同,因为在撰写本文时还没有 Mac 版的 SQL Server。
在 Mac 上运行 SQL Server 的唯一方法是安装 Windows 虚拟机并从其中运行。然而,随着 SQL Server 2017 的发布,Microsoft 已将其提供给 macOS 和 Linux 环境。这使得我们可以使用 Docker 容器来运行 SQL Server,就像服务器运行在你的 Mac 上一样。感谢 Docker!
Linux 版 SQL Server 先决条件
根据文档,Linux 版 SQL Server 至少需要 2 GB 的磁盘空间才能运行。但是,Docker 默认只分配 2 GB。因此,我们应该增加此分配以确保 SQL Server 能够运行。为此,请按照以下步骤操作
- 打开 Docker 应用程序。
- 使用你的 Docker 凭据登录。
- 在 Docker 主菜单中,选择偏好设置。
- 单击高级选项卡,并将内存分配调整到 4 GB 左右,如下图所示图 2:Docker 内存分配
- 单击应用并重启。
现在,通过在终端控制台中运行以下命令,从Docker Hub下载 Linux 版 SQL Server Docker 容器镜像
docker pull mcr.microsoft.com/mssql/server:2017-latest
docker pull
命令从 Docker 注册表中下载特定的镜像或一组镜像。下图显示了你的拉取成功的情况
要确认我们拉取的镜像,请运行
docker images
上面的命令应该产生类似这样的结果
然后,我们可以通过运行以下命令来开始在 Docker 中运行 SQL Server 容器镜像
docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=SuperSecret1!'
-p 1433:1433 --name sql17_linux -d mcr.microsoft.com/mssql/server:2017-latest
请记下 SA_PASSWORD
值,因为我们将使用它来设置 ConnectionString
以便我们能够与数据库通信。
下表提供了在之前的 docker run 示例中摘自官方文档的参数说明
参数 | 描述 |
-e 'ACCEPT_EULA=Y' | 将 ACCEPT_EULA 变量设置为任何值,以确认你接受最终用户许可协议。SQL Server 镜像的必需设置。 |
-e 'SA_PASSWORD=SuperSecret1!' | 指定你自己的强密码,该密码至少有 8 个字符,并满足SQL Server 密码要求。SQL Server 镜像的必需设置。 |
-p 1433:1433 | 将主机环境上的 TCP 端口(第一个值)与容器中的 TCP 端口(第二个值)映射。在此示例中,SQL Server 在容器中的 TCP 1433 上侦听,并将其暴露给主机上的端口 1433。 |
--name sql17_linux | 指定容器的自定义名称,而不是随机生成的名称。如果你运行多个容器,则不能重复使用相同的名称。 |
-d microsoft/mssql-server-linux:2017-latest | 在分离模式下运行 SQL Server 2017 Linux 容器镜像。 |
要验证 Docker 容器是否正在运行,请运行
docker ps
它应该会给出以下结果
如果 Linux 版 SQL Server Docker 容器退出,你可以使用以下命令再次运行它
docker run -e 'SA_PASSWORD=SuperSecret1!' -p 1433:1433
-d mcr.microsoft.com/mssql/server:2017-latest
连接到 Docker 容器内的 SQL Server
打开 Valentina Studio。首次打开时,你可能会看到一个输入密钥的提示,如下图所示
选择获取免费序列号并输入,然后单击继续,它将带你到下一个屏幕
输入所有必需的信息,然后单击继续。当你的注册成功后,你应该会收到一封来自 Valentina 支持的电子邮件,其中包含 Mac、Linux 和 Windows 的免费序列号。
配置 Valentina 使用免费序列号后,你应该会看到类似下图的内容
在“服务器”面板的底部,单击添加书签,它将为你显示以下屏幕
输入用于在上一步中连接 Docker 容器中托管的 Linux 版 SQL Server 的书签名称、端口和密码。首先单击“测试连接”以确保你输入的信息正确,如果测试成功,请单击确定以保存书签,这是一个好习惯。
保存书签后,你将在“服务器”面板下看到一个新项目,允许你直接访问 Docker SQL 实例,如下图所示
引用请注意,你也可以使用容器内的 SQL Server 命令行工具 sqlcmd 连接到 SQL Server。有关更多信息,请参阅:https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-2017
创建空数据库
现在是时候创建新数据库了。双击我们之前创建的数据库实例。它应该显示类似这样的内容
单击打开 SQL 编辑器,然后执行下面的脚本
CREATE DATABASE Band;
上面的脚本应该会创建一个名为“Band
”的空白数据库,如下图所示
请注意,你也可以通过单击创建数据库按钮使用 Valentina Studio UI 来创建和管理数据库。
此时,我们已准备好实现基本的数据访问。
创建 ASP.NET Core Web API 项目
打开 Visual Studio for Mac,然后创建一个新项目。在.NET Core > App 下,选择ASP.NET Core Web API,如下图所示
单击下一步,它应该会显示以下屏幕
输入项目名称、解决方案名称和位置。你还可以配置版本控制,但这是可选的。单击创建,让 Visual Studio 生成应用程序所需的默认文件。下面是解决方案的样子
让我们快速概述一下生成的文件。
如果你已经了解 ASP.NET Core 的核心重要更改,则可以跳过此部分,但如果你是 ASP.NET Core 的新手,我想强调其中一些更改。如果你之前使用过 ASP.NET 的旧版本,那么你会注意到新的项目结构完全不同。项目现在包含这些文件
- Dependencies:包含应用程序所需的 NuGet 和 SDK 依赖项。
- Controllers:这是你放置所有 Web API 或 MVC 控制器类的地方。请注意,Web API 项目默认会生成 ValuesController.cs 文件。
- Properties:包含 launchSettings.json 文件,该文件管理与每个调试配置文件(如 IIS、IIS Express 和应用程序本身)相关的应用程序配置设置。这是你为应用程序中使用的框架定义特定于配置文件的配置(编译和调试配置文件)的地方。
- wwwroot:这是一个文件夹,你将在其中放置所有静态文件。这些是 Web 应用将直接提供给客户端的资产,包括 HTML、CSS、图像和 JavaScript 文件。
- appsettings.json:包含应用程序设置。
- Startup.cs:这是你放置启动和配置代码的地方。
- Program.cs:这是你初始化应用程序所需所有服务的地方。
首次运行
为确保我们拥有 Web API 项目所需的一切,让我们尝试构建并运行项目。我们可以通过按Command + Return键或单击 Visual Studio 菜单工具栏上的播放按钮来完成此操作。这将编译、构建并自动启动浏览器窗口。如果你看到下面的结果,那么我们可以安全地假设我们的项目可以正常工作。
很棒!现在让我们继续下一步。
使用 Entity Framework Core 配置数据访问
让我们将 Entity Framework Core 包安装到我们的项目中。从 Visual Studio 菜单,转到项目>添加 Nuget 包。或者,你可以右键单击项目的Dependencies文件夹,然后选择添加包。
下图显示了 Nuget 包管理器窗口
在搜索栏中,键入“Microsoft.EntityFrameworkCore.SqlServer
”,然后单击添加包。
稍后在本演示中,我们将使用一些 Entity Framework 工具来维护数据库。所以也要安装以下工具包
Microsoft.EntityFrameworkCore.Tools
截至撰写本文时,Microsoft.EntityFrameworkCore.SqlServer
和 Microsoft.EntityFrameworkCore.Tools
的版本是2.1.3。
成功安装上述包后,请务必检查你的项目 NuGet 依赖项,以确保我们已将它们包含在项目中。请参见下图
这些包用于提供 Entity Framework Core 迁移。迁移使我们能够根据我们的实体模型生成数据库、同步和更新表。
本次演示,我们将使用 Code-First 方法,即我们创建类(POCO 实体)并从中生成新的数据库。如果你是 Entity Framework Core 的新手,请在此阅读我的另一篇文章:Entity Framework Core 入门:使用 Web API 和 Code First 开发构建 ASP.NET Core 应用程序。
创建模型
在你的应用程序中创建一个新文件夹,并将其命名为“Models”。在该文件夹下,创建一个新类并将其命名为“Band
”,然后复制代码
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Band.API.Models
{
public class Band
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Genre { get; set; }
}
}
Band
类只是一个简单的类,包含一些属性。你可能注意到我们用 [Key]
和 [DatabaseGenerated]
属性装饰了 Id
属性。这是因为我们将把这个类转换为数据库模式,而 Id
属性将作为我们的主键,具有自动递增的标识。这些属性位于 System.ComponentModel.DataAnnotations
中。有关数据注解的更多详细信息,请参阅 使用数据注解进行模型验证。
定义 DBContext
Entity Framework Core Code-First 开发方法要求我们创建一个继承自 DbContext
类的数据库访问上下文类。现在,让我们在“Models”文件夹下添加一个新类。将该类命名为“BandContext”,然后复制代码
using System;
using Microsoft.EntityFrameworkCore;
namespace Band.API.Models
{
public class BandContext : DbContext
{
public BandContext(DbContextOptions opts) : base(opts){}
public DbSet<Band> Bands { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//Database.EnsureCreated();
}
}
}
上面的类定义了一个上下文和一个实体类,构成了我们的模型。DbContext
必须有一个 DbContextOptions
实例才能执行。这可以通过重写 OnConfiguring()
方法或通过构造函数参数从外部提供。在我们上面的示例中,我们选择通过构造函数参数
来配置我们的DbContext
。
添加数据库迁移
我们的下一步是添加 Code-First 迁移。迁移根据我们的模型自动创建数据库。我们将使用 EF Core 工具来脚手架迁移并更新数据库。我们将使用命令行(dotnet CLI)来运行我们的迁移。
设置连接字符串
让我们在 appsettings.json 文件中定义以下数据库连接字符串
"ConnectionString": "Server=127.0.0.1; Database=Band; User Id=sa;Password=SuperSecret1!;"
上面的 ConnectionString
定义了一组属性,用于将应用程序连接到我们之前配置的数据库。这些属性是
- Server:我们使用 IP 地址连接到服务器,使用 127.0.0.1,它等同于 localhost。请注意,放置 1433 端口不是必需的,因为它是连接到 Linux 版 SQL Server 实例的默认端口号。
- Database:是要连接的数据库的名称。在这种情况下,是
Band
数据库。 - User Id:用于连接 SQL Server 的用户 ID。在这种情况下是“
SA
”。 - Password:用于连接 Docker 容器中 Linux 版 SQL Server 的密码。在这种情况下,是“
SuperSecret1
!”
注册 DBConext 作为服务
接下来,我们需要将 BandContext
注册为服务,并启用它使用 SQL Server。为此,请按照以下步骤操作
- 打开 Startup.cs 并声明以下命名空间
using Microsoft.EntityFrameworkCore; using BandModel = Band.API.Models;
- 接下来,在
ConfigureServices()
方法中添加以下代码,将BandContext
注册为服务public void ConfigureServices(IServiceCollection services){ services.AddDbContext<BandModel.BandContext>(opts => opts.UseSqlServer (Configuration["DbContextSettings:ConnectionString"])); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }
- 清理并构建应用程序以确保没有错误。
执行数据库迁移
现在,打开终端控制台到你的项目解决方案所在的位置,然后执行
dotnet ef migrations add InitialMigration
dotnet ef migration
是用于添加迁移的命令。迁移成功后,你应该会看到一个名为“Migrations”的新文件夹,其中包含迁移文件,如下图所示
此时,我们的实体模型(Bands DbSet
)尚未添加到数据库。我们需要应用迁移到数据库以创建模式,方法是在终端控制台中运行以下命令
dotnet ef database update
上面的命令应该会同步实体模型到数据库。你可以在 Valentina Studio 中确认这一点,如下图所示
你也可以使用 SQLCMD
命令行工具来验证迁移是否已反映在 Linux 版 SQL Server 的 Docker 容器中,方法是在终端控制台中运行以下命令
docker exec -it sql17_linux /opt/mssql-tools/bin/sqlcmd
-S localhost -U sa -P SuperSecret1!
然后运行以下命令
1> SELECT Name FROM sys.Databases
2> GO
这应该会得到这样的结果
然后你可以通过运行以下命令来深入查看模式
1> USE band
2> GO
1> SELECT * FROM Bands
2> GO
它应该显示我们已迁移的列,但当然,目前它应该返回空结果。
在应用程序启动时填充测试数据
在某些情况下,你可能不想在添加迁移后手动生成数据库和模式。这可以通过在运行时应用迁移来实现。为此,让我们设置一个助手类来生成默认测试数据。
继续在“Models”文件夹下创建一个新文件夹,然后创建一个名为“DBSeeder
”的新类,并将生成的默认代码替换为以下内容
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
namespace Band.API.Models.DataManager
{
public class DBSeeder
{
public static void Seed(IApplicationBuilder appBuilder)
{
using (var serviceScope = appBuilder.ApplicationServices.CreateScope())
{
var context = serviceScope.ServiceProvider.GetService<BandContext>();
using (context)
{
context.Database.Migrate();
if (!context.Bands.Any())
{
var bands = new List<Band>()
{
new Band(){
Name = "Alice In Chains",
Genre="Heavy Metal"
}
};
context.Bands.AddRange(bands);
context.SaveChanges();
}
}
}
}
}
}
Database.Migrate()
方法负责两件事
- 如果数据库尚不存在,则在 SQL Server 中创建数据库。
- 将数据库模式迁移到最新版本。
最后一步是在应用程序启动时应用迁移。为此,你可以在 Web API 项目的 Startup
类中的 Configure()
方法中调用 DBSeeder.Seed()
方法
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
DBSeeder.Seed(app);
app.UseHttpsRedirection();
app.UseMvc();
}
引用请注意,此方法不适合所有人。虽然对于具有本地数据库的应用程序来说它很棒,但大多数应用程序将需要更强大的部署策略,例如生成 SQL 脚本。
实现数据存储库接口
我们不希望我们的 API 控制器直接访问我们的 BandContext
服务,而是让其他服务处理我们 BandContext
和 API 控制器之间的通信。因此,我们将实现一个基本的通用存储库来处理我们应用程序中的数据访问。在“Models”下添加一个新文件夹,并将其命名为“Repository”。创建一个新的 interface
并将其命名为“IDataRepository
”。更新该文件内的代码,使其类似于下面的代码
using System.Collections.Generic;
namespace Band.API.Models.Repository
{
public interface IDataRepository<TEntity, U> where TEntity : class
{
IEnumerable<TEntity> GetAll();
TEntity Get(U id);
int Add(TEntity b);
int Update(U id, TEntity b);
int Delete(U id);
}
}
上面的代码定义了我们的 IDataRepository
接口。接口只是一个没有实际实现的方法的骨架。此接口将被注入到我们的 API 控制器中,因此我们只需要与接口交互,而不是与存储库的实际实现交互。使用接口的一个主要好处是使我们的代码可重用且易于维护。
创建数据管理器
接下来,我们将创建一个实现 IDataRepository
接口的具体类
。在“Models”下添加一个新文件夹,并将其命名为“DataManager”。创建一个新类并将其命名为“BandManager
”。更新该文件内的代码,使其类似于下面的代码
using Band.API.Models.Repository;
using System.Collections.Generic;
using System.Linq;
namespace Band.API.Models.DataManager
{
public class BandManager: IDataRepository<Band, int>
{
BandContext _context;
public BandManager(BandContext context)
{
_context = context;
}
public Band Get(int id)
{
return _context.Bands.FirstOrDefault(b => b.Id == id);
}
public IEnumerable<Band> GetAll()
{
return _context.Bands.ToList();
}
public int Add(Band band)
{
_context.Bands.Add(band);
int id = _context.SaveChanges();
return id;
}
public int Delete(int id)
{
var band = _context.Bands.FirstOrDefault(b => b.Id == id);
if (band != null)
{
_context.Bands.Remove(band);
_context.SaveChanges();
}
return id;
}
public int Update(int id, Band item)
{
var band = _context.Bands.Find(id);
if (band != null)
{
band.Name = item.Name;
band.Genre = item.Genre;
_context.SaveChanges();
}
return id;
}
}
}
DataManager
类处理我们应用程序的所有数据库操作。此类旨在将实际数据操作逻辑与我们的 API 控制器分离,并成为处理创建、更新、获取和删除(CRUD)操作的中心类。
目前,DataManager
类包含五个方法
Get()
方法通过传递Id
从数据库中获取特定的学生记录。它使用LINQ
语法查询数据并返回一个Band
对象。GetAll()
方法获取数据库中的所有乐队记录,正如你可能从方法名称中猜测的那样。Add()
方法在数据库中创建一个新的乐队记录。Delete()
方法根据Id
从数据库中删除特定的乐队记录。- 最后,
Update()
方法更新数据库中的特定学生记录。
以上所有方法都使用LINQ
从数据库查询数据。
将 IDataRespository 接口注册为服务
接下来,我们将 IDataRepository
注册为服务。此服务将在应用程序启动期间使用依赖注入(DI)进行注册。这将使我们的 API 控制器或其他服务能够通过构造函数参数或属性访问 BandContext
。
为了让其他服务能够使用 BandContext
,我们将将其注册为服务。要启用该服务,请执行以下步骤
- 打开 Startup.cs 并声明以下命名空间
using Band.API.Models.Repository; using Band.API.Models.DataManager;
- 在
services.AddDbContext()
和services.AddMvc()
方法之间,在ConfigureServices()
方法中添加以下代码services.AddScoped(typeof(IDataRepository<BandModel.Band, int>), typeof(BandManager));
创建 API 控制器
现在我们的 DataManager
已准备就绪,并且为 IDataRepository
启用了依赖注入,现在是时候创建 API 控制器并公开一些端点来为客户端提供数据了。
右键单击“Controllers”文件夹,然后选择添加>新建文件>ASP.NET Core>Web API 控制器类,如下图所示
将类命名为“BandsController
”,然后单击新建。
现在替换生成的默认代码,使其类似于下面的代码
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using Band.API.Models.Repository;
using BandModel = Band.API.Models;
namespace Band.API.Controllers
{
[Route("api/[controller]")]
public class BandsController : Controller
{
private IDataRepository<BandModel.Band, int> _iRepo;
public BandsController(IDataRepository<BandModel.Band, int> repo)
{
_iRepo = repo;
}
// GET: api/bands
[HttpGet]
public IEnumerable<BandModel.Band> Get()
{
return _iRepo.GetAll();
}
// GET api/bands/5
[HttpGet("{id}")]
public BandModel.Band Get(int id)
{
return _iRepo.Get(id);
}
// POST api/bands
[HttpPost]
public int Post([FromBody]BandModel.Band band)
{
return _iRepo.Add(band);
}
// POST api/bands
[HttpPut]
public int Put([FromBody]BandModel.Band band)
{
return _iRepo.Update(band.Id, band);
}
// DELETE api/bands/5
[HttpDelete("{id}")]
public int Delete(int id)
{
return _iRepo.Delete(id);
}
}
}
让我们看看我们刚刚做了什么。
BandController
派生自 Controller
基类,并通过为其添加 Route
属性,使此类成为 Web API Controller
。
如果你注意到,我们使用了Controller Injection
来订阅IDataRepository
接口。所以,我们不是直接与StudentManager
类交互,而是简单地与接口交互并利用可用的方法。
BandController
具有五个(5)个主要方法/端点。第一个 Get()
方法调用 IDataRepository
接口的 GetAll()
方法,并基本上返回所有列表Band来自数据库。第二个 Get()
方法返回一个特定的Band基于 Id
的对象。请注意,第二个 Get()
方法带有 [HttpGet("{id}")]
装饰,这意味着将“id
”附加到路由模板。"{id}"
是 Band
对象 Id
的占位符变量。当调用第二个 Get()
方法时,它会将 URL 中的“{id}
”值分配给方法的 id 参数。Post()
方法在数据库中添加新记录。Put()
方法更新数据库中的特定记录。请注意,Post()
和 Put()
都使用了[FromBody]
标签。当参数具有[FromBody]
时,Web API 使用Content-Type header
来选择格式化程序。最后,Delete()
方法从数据库中删除特定记录。
启用 CORS
现在我们的 API 已准备就绪,在此项目上我们将要完成的最后一步是启用跨域资源共享(也称为 CORS)。我们需要此配置,以便托管在不同域/端口上的其他客户端可以访问 API 端点。
要在 ASP.NET Core Web API 中启用 CORS,请按照以下步骤操作
- 在
Startup.cs
文件中,在services.AddMvc()
之前,于ConfigureServices()
方法中添加以下代码services.AddCors(opts =>{ opts.AddPolicy("AllowAll", builder => { builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials(); }); });
- 然后在
Configure()
方法中,在app.UseMvc()
之前复制以下代码app.UseCors("AllowAll");
就这样。
引用请注意,仅出于本次演示的目的,我们允许所有客户端访问 API。在实际应用程序中,建议为你的 API 设置允许的来源、方法、标头和凭据。有关 CORS 的更多信息,请参阅:https://docs.microsoft.com/en-us/aspnet/core/security/cors?view=aspnetcore-2.1
测试 API 端点
为了确保我们的 API 端点能够正常工作,在开发早期阶段进行初始测试总是推荐的。你可以下载Postman并将其用作测试 API 的工具。
以下是我在 Postman 中进行的一些测试
POST cURL
:
curl -X POST \
https://:5001/api/bands \
-H 'Cache-Control: no-cache' \
-H 'Content-Type: application/json' \
-H 'Postman-Token: a025f464-3501-4fbc-a556-51ed1f4abe0c' \
-d '{
"Name": "Alice in Chains",
"Genre": "Heavy Metal"
}'
输出
GET cURL
:
curl -X GET \
https://:5001/api/bands \
-H 'Cache-Control: no-cache' \
-H 'Postman-Token: 40bbcadd-c5fe-43da-8c4f-fb89bc63f9b8'
输出
Docker 化 ASP.NET Core Web API 应用程序
此时,Web API 应用程序仍在本地计算机上运行。我们的目标是将其部署到 Docker 容器中,以便其他客户端应用程序可以访问它。
启用 Docker 支持
要启用 Docker 支持,请右键单击 Web API 项目,然后选择添加>添加 Docker 支持,如下图所示
这应该会将以下文件添加到项目中
- Dockerfile - 包含构建 Docker 镜像的说明。有关更多信息,请参阅:Dockerfile 参考
- Docker-Compose - 用于定义和运行多容器 Docker 应用程序的工具。有关更多信息,请参阅:Docker Compose
Docker 文件
本次演示的 Docker 文件看起来是这样的
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base
WORKDIR /app
EXPOSE 5555
FROM microsoft/dotnet:2.1-sdk AS build
WORKDIR /src
COPY Band.API/Band.API.csproj Band.API/
RUN dotnet restore Band.API/Band.API.csproj
COPY . .
WORKDIR /src/Band.API
RUN dotnet build Band.API.csproj -c Release -o /app
FROM build AS publish
RUN dotnet publish Band.API.csproj -c Release -o /app
FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Band.API.dll"]
上面的指令包含创建最终 Docker 镜像的配方。它初始化 多阶段构建镜像,并为后续命令设置基础镜像。在这种情况下,它告诉 Docker 将我们的项目基于 aspnetcore-runtime 镜像并在那里运行,暴露端口 5555
。后续指令告诉 Docker 拉取此应用程序运行所需的依赖项,构建它并将其发布为容器。有关其中命令的更多信息,请参阅:Dockerfile 参考。
管理容器
请记住,我们的 SQL Server 数据库运行在单独的容器中。我们现在需要做的是,当 Web API 应用和数据库都位于 Docker 容器内部时,让 Web API 应用能够连接到数据库。不幸的是,使用 ConnectionString
服务器值 127.0.0.0,1433
连接到已运行的容器将不起作用。
虽然有可能链接一个现有的容器并与你的 Web API 容器一起运行它,如我在之前的文章 这里 中所演示的那样,但对于处理相互依赖的多容器应用程序来说,这仍然不是理想的处理方式。
Docker-Compose 文件
幸运的是,Visual Studio 2017 版本 15.7 或更早版本支持 Docker Compose 作为唯一的容器编排解决方案。当我们启用 Docker 支持时,Docker Compose 工件会自动为我们添加,如前一节所述。
Docker Compose 为我们提供了一种在同一位置更方便地管理多个 Docker 容器的方法。这是此应用程序的 docker-compose.yaml 文件外观
version: '3.4'
services:
band.api:
image: ${DOCKER_REGISTRY}band
container_name: band.api
ports:
- "5555:80"
build:
context: .
dockerfile: Band.API/Dockerfile
environment:
- ConnectionString=Server=band.data;Database=Band;User Id=sa;
Password=SuperSecret1!
depends_on:
- band.data
band.data:
image: mcr.microsoft.com/mssql/server:2017-latest
container_name: band.data
environment:
- SA_PASSWORD=SuperSecret1!
- ACCEPT_EULA=Y
ports:
- "1433:1433"
此文件中的根键是“services
”。在该键下,你定义了当你使用命令执行 docker-compose 或从 Visual Studio 使用此 docker-compose.yml 文件进行部署时想要部署和运行的服务。在这种情况下,docker-compose.yml 文件定义了多个服务,如下列表所述。
band.api
– 定义 ASP.NET Core Web API 项目的容器。ports
属性将5555
暴露给外部访问应用程序,并将80
暴露给 Docker 内部访问应用程序。换句话说,它提供了网络管道,以便我们可以通过映射到主机上的端口来与容器中运行的服务通信。build
属性包含指向dockerfile
的路径,该dockerfile
将用于构建容器,以及 Docker 应该用来从该dockerfile
构建容器的上下文。environment
属性定义了一个名为ConnectionString
的变量,其中包含 Entity Framework 用于访问 Docker 容器内 SQL Server 实例的连接字符串。此配置将覆盖我们在 Web API 项目的 appsettings.json 中定义的ConnectionString
值。此外,请注意,它使用band.data
作为服务器值而不是 IP 地址。这是 docker compose 的优点,因为它使我们能够在不担心内部 IP 和端口的情况下连接容器。最后,depends_on
属性定义了对其他容器的依赖。在这种情况下,是band.data
。这告诉 Docker 在运行band.api
服务之前先运行band.data
服务。band.data
– 定义如何运行 Linux 版 SQL Server Docker 容器。此 Docker Compose 配置与使用我们之前使用此命令运行的docker run
命令来运行镜像的配置相同
docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=SuperSecret1!'
-p 1433:1433 --name sql17_linux -d mcr.microsoft.com/mssql/server:2017-latest
执行 Docker Compose
要构建和部署 docker-compose.yaml 文件中定义的容器,请执行以下步骤
- 将 Web API 项目清理并构建为发布模式。
- 打开 终端控制台,确保你正在解决方案文件所在的位置运行它。
- 使用命令停止现有的正在运行的 Linux 版 SQL Server 容器:
docker stop sql17_linux
- 然后运行:
docker ps
以确保名为“sql17_linux
”的容器已停止。 - 接下来,运行:
docker-compose up -d
。
构建成功时,你应该能看到类似下图的内容
测试 Docker 化 Web API
此时,Web API 应用和数据库应该已经在 Docker 容器中启动并运行。我们可以通过使用我们在 dockerfile 和 docker-compose.yaml 中定义的外部端口 5555
来访问它。打开浏览器或 Postman,然后访问以下 URL
这将输出以下数据
如果你能走到这一步,恭喜你!你已经在 Mac 上的 Docker 中运行了 ASP.NET Core 和 SQL Server。是不是很酷?
但我们还没有完成!我们仍然需要一个 UI 来使用服务器端 SPA 框架 Blazor 来消费 API!
在 Docker 上创建你的第一个 Blazor 应用
引用本演示适用于 Blazor 0.6.0,因为这是撰写本文时最新版本。任何代码、文件结构和语法都可能在未来版本中更改。在此处查看官方仓库以获取未来版本更新:https://github.com/aspnet/Blazor
闲话少说,让我们开始吧。截至撰写本文时,Visual Studio 2017 Community v7.6 for MAC 不包含 Blazor 模板,因此你需要使用 dotnet new CLI 手动安装它们,如下所示
dotnet new -i Microsoft.AspNetCore.Blazor.Templates::*
上面的命令应该会安装用于构建和运行 Blazor 应用的模板。你可以通过运行以下命令来验证安装是否成功
dotnet new --list
这应该会产生以下结果
请注意,即使你已从命令行 (CLI) 添加了 Blazor 模板,它们也不会出现在 Visual Studio for Mac 中创建新项目时的 .NET Core Apps 列表中。这意味着,为了创建新的 Blazor 项目,我们也需要使用命令行。
为 Blazor 创建运行时 Docker 容器
由于我们的目标是在 Docker 容器中运行 Blazor,那么我们将采取不同的路线。这仅仅意味着我们将首先构建一个 Docker 容器运行时环境,因为截至目前,我所知的还没有现成的。
为了构建 Blazor 的 Docker 容器运行时,我们需要确保 .NET Core SDK 的确切支持版本与你正在使用的 Blazor 版本相匹配。在此示例中,我们将使用Blazor 0.6.0
,它需要.NET Core 2.1 SDK (2.1.402 或更高版本)
。一旦我们确定了这一点,请前往 .NET Core Docker 容器的官方仓库 此处。
该链接应该会带你到一个页面,其中列出了所有可用的 Linux 和 Windows Nano Server 的 .NET Core 和 ASP.NET Core 的官方镜像。下图显示了常用标签的最新版本
单击2.1-sdk,它应该会带你到相应Dockerfile的 GitHub 仓库。
从仓库复制 Dockerfile
的内容。在本地驱动器上创建一个新的 Dockerfile
并将内容粘贴在那里。我的 Dockerfile
看起来是这样的
FROM buildpack-deps:stretch-scm
# Install .NET CLI dependencies
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libc6 \
libgcc1 \
libgssapi-krb5-2 \
libicu57 \
liblttng-ust0 \
libssl1.0.2 \
libstdc++6 \
zlib1g \
&& rm -rf /var/lib/apt/lists/*
# Install .NET Core SDK
ENV DOTNET_SDK_VERSION 2.1.403
RUN curl -SL --output dotnet.tar.gz
https://dotnetcli.blob.core.windows.net/dotnet/Sdk/$DOTNET_SDK_VERSION/
dotnet-sdk-$DOTNET_SDK_VERSION-linux-x64.tar.gz \
&& dotnet_sha512='903a8a633aea9211ba36232a2decb3b34a59bb62bc145a0e7a90c
a46dd37bb6c2da02bcbe2c50c17e08cdff8e48605c0f990786faf1f06be1ea4a4d373beb8a9' \
&& sha512sum dotnet.tar.gz \
&& echo "$dotnet_sha512 dotnet.tar.gz" | sha512sum -c - \
&& mkdir -p /usr/share/dotnet \
&& tar -zxf dotnet.tar.gz -C /usr/share/dotnet \
&& rm dotnet.tar.gz \
&& ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet \
&& dotnet new -i Microsoft.AspNetCore.Blazor.Templates
# Configure Kestrel web server to bind to port 80 when present
ENV ASPNETCORE_URLS=http://+:80 \
# Enable detection of running in a container
DOTNET_RUNNING_IN_CONTAINER=true \
# Enable correct mode for dotnet watch (only mode supported in a container)
DOTNET_USE_POLLING_FILE_WATCHER=true \
# Skip extraction of XML docs - generally not useful
# within an image/container - helps performance
NUGET_XMLDOC_MODE=skip
# Trigger first run experience by running arbitrary cmd to populate local package cache
RUN dotnet help
上面的脚本包含了在 Docker 容器中安装和配置 .NET Core 的指令。其中一条重要线是我添加了(dotnet new -i Microsoft.AspNetCore.Blazor.Templates
)命令来获取 Blazor 模板,这样我们就无需担心手动安装它们,而可以专注于直接构建 Blazor 应用。
现在,保存 Dockerfile,然后打开你放置 Docker 文件的终端控制台,然后运行以下命令
docker build -t blazor060:core2.1.402 .
上面的命令会创建一个名为 blazor060:core2.1.402
的本地 Docker 镜像。请注意,可以使用以下格式 <Your Image Name>:<Tag>
来配置名称。
构建成功后,你就可以运行命令 docker images
来验证它是否已在你的本地机器上创建。
替代方案
如果你不想构建和创建自己的 Docker 容器运行时环境,也可以使用我为此演示创建的现有 Docker 镜像 此处。
然后你可以这样做
docker pull proudmonkey30/blazor060
创建 Blazor 应用
此时,我们已准备好创建 Blazor 应用。请转到你的本地驱动器,在你想存储 Blazor 应用的位置创建一个新目录。在此示例中,我在 Mac 的 /<User>/repo/Blazor.Spa 上创建了一个文件夹。
之后,打开你的终端控制台并运行以下命令
docker run -it --rm --name blazorapp -p 5600:80
-v /Users/vdurano_srg/repo/Blazor.Spa/:/BlazorApp -w /BlazorApp blazor060:core2.1.402
如果你使用的是我创建的替代 Docker 运行时,你可以这样做
docker run -it --rm --name blazorapp -p 5600:80
-v /Users/vdurano_srg/repo/Blazor.Spa/:/BlazorApp -w /BlazorApp proudmonkey30/blazor060
上面的命令以交互模式运行 Docker 容器。–rm
命令告诉 Docker 在退出后删除容器。–name
指定容器名称。-p
命令设置运行中容器的端口,在这种情况下,我们将外部端口设置为 5600
,内部端口设置为 80
。-v
命令将卷附加到容器,在这种情况下,我们将卷设置为我们存储 Blazor 相关文件的本地目录,然后将其映射到名为“/BlazorApp
”的内部 Docker 容器位置。-w
命令指定一个工作目录,在这种情况下,我们希望在执行命令后自动将工作目录设置为“/Blazor
”。最后,我们基于早期创建的镜像来下载和恢复依赖项。
现在打开一个新的终端控制台窗口并运行命令 docker ps,它应该会产生类似这样的结果
回到我们执行 docker run
命令的第一个终端控制台窗口,然后执行
dotnet new blazor -o .
上面的命令应该会拉取 Blazor 模板,并将默认文件创建到我们设置的作为卷的本地目录中,如下图所示
现在,让我们尝试运行应用程序,执行
dotnet run
成功时,它应该在控制台中显示类似这样的内容
请记住,我们是在 Docker 容器内运行应用程序,因此你看到它监听的是端口 80
。但是,我们可以通过外部端口 5600
来测试它。要查看实际效果,请打开 Chrome 浏览器并导航到 https://:5600。它应该在你的浏览器中显示类似这样的内容
太棒了!你刚刚在 Mac 上运行了第一个 Docker 化 Blazor 应用!现在让我们做些有趣的事情,修改应用程序以使用我们部署在 Docker 中的 Web API 的真实数据。
现在回到终端控制台,按 CTRL + C 停止正在运行的应用程序。
我在这里不会详细介绍 Blazor 应用是如何实现的。我将在另一篇文章中介绍。本文的主要目标是了解如何如何在 Docker 容器中运行、构建和部署应用程序,以及它们之间的连接。
好的,让我们继续,打开我们之前创建的 Blazor 应用。要在 Mac 上打开多个 Visual Studio 实例,请参阅 此链接。
添加新组件
让我们通过右键单击“Pages”文件夹并选择添加>新建项来添加一个新组件。从左侧面板选择ASP.NET Core,然后在模板面板中选择“Razor Page”,如下图所示
将文件命名为“MyFavoriteBands”,然后单击新建。复制代码
@page "/bands" @using Microsoft.AspNetCore.Blazor; @using Microsoft.JSInterop; @using System.Threading.Tasks; @inject HttpClient Http <h1>All-time Favorite Bands</h1> <div> <div class="row"> <div class="col-sm-1"> <p>Name:</p> </div> <div class="col-sm-4"> <input id="txtBandName" placeholder="Band Name" bind="@_bandName" /> </div> </div> <br /> <div class="row"> <div class="col-sm-1"> <p>Genre:</p> </div> <div class="col-sm-4"> <input id="txtBandGenre" placeholder="Band Genre" bind="@_bandGenre" /> </div> </div> <br /> <div class="row"> <div class="col-sm-1"> <button class="btn btn-info" id="btnAdd" onclick=@(async () => await Add())>Add</button> </div> </div> <br /> </div> @if (bands == null) { <p><em>Loading your favorite bands of all time...</em></p> } else { @if (bands.Count > 0) { <table class='table table-striped table-bordered table-hover table-condensed' style="width:80%;"> <thead> <tr> <th style="width: 40%">Band Name</th> <th style="width: 20%">Band Genre</th> <th style="width: 20%">Edit</th> <th style="width: 20%">Delete</th> </tr> </thead> <tbody> @foreach (var band in bands) { <tr> <td> <span id="spnName_@band.Id">@band.Name</span> <input id="txtName_@band.Id" bind="@_bandNameUpdate" style="display:none;"></input> </td> <td> <span id="spnGenre_@band.Id">@band.Genre</span> <input id="txtGenre_@band.Id" bind="@_bandGenreUpdate" style="display:none;"></input> </td> <td> <button id="btnEdit_@band.Id" class="btn btn-primary" onclick=@(async() => await Edit(band.Id, band.Name, band.Genre))>Edit</button> <button id="btnUpdate_@band.Id" style="display:none;" class="btn btn-success" onclick=@(async () => await Update(band.Id))>Update</button> <button id="btnCancel_@band.Id" style="display:none;" class="btn btn-primary" onclick=@(async () => await Cancel(band.Id))>Cancel</button> </td> <td><button class="btn btn-danger" onclick=@(async () => await Delete(band.Id))>Delete</button></td> </tr> } </tbody> </table> } } @functions { public class BandDTO { public int Id { get; set; } public string Name { get; set; } public string Genre { get; set; } } string _bandAPIUri = "https://:5555/api/bands"; string _bandName; string _bandGenre; string _bandNameUpdate; string _bandGenreUpdate; IList<BandDTO> bands = new List<BandDTO>(); protected override async Task OnInitAsync() { await RefreshView(); } private async Task RefreshView() { bands = await Http.GetJsonAsync<BandDTO[]>(_bandAPIUri); StateHasChanged(); } public async Task Add() { if (!string.IsNullOrEmpty(_bandName)) { await Http.SendJsonAsync(HttpMethod.Post, _bandAPIUri, new BandDTO { Name = _bandName, Genre = _bandGenre }); _bandName = string.Empty; _bandGenre = string.Empty; await RefreshView(); } } public async Task Update(int id) { if (!string.IsNullOrEmpty(_bandNameUpdate)) { await Http.SendJsonAsync(HttpMethod.Put, _bandAPIUri, new BandDTO { Id = id, Name = _bandNameUpdate, Genre = _bandGenreUpdate }); await RefreshView(); await JSRuntime.Current.InvokeAsync<bool>("toggleUIView", new object[] { id.ToString(), "", "", false }); } } public async Task Delete(int id) { await Http.DeleteAsync($"{_bandAPIUri}/{id}"); await RefreshView(); } public async Task Edit(int id, string bandName, string bandGenre) { await JSRuntime.Current.InvokeAsync<bool>("blazorAppJS.toggleUIView", new object[] { id.ToString(), bandName, bandGenre, true }); } public async Task Cancel(int id) { await JSRuntime.Current.InvokeAsync<bool>("blazorAppJS.toggleUIView", new object[] { id.ToString(), "", "", false }); } }
上面的组件使用Razor和C#语法定义 UI 和相应的 UI 代码逻辑,通过利用 REST API 在页面上执行基本的 CRUD 操作。请注意,bandAPIUri
包含我们在 Docker 中部署的 Web API 项目的外部/面向公众的 URL 端点。
修改 Index.html 文件
接下来,导航到 wwwroot > index.html,然后在 blazor.webassemly.js 脚本引用后复制代码
<script>
window.blazorAppJS = {
toggleUIView: function(id, name, genre, show){
if(show){
var txtName = document.getElementById("txtName_" + id);
document.getElementById("spnName_" + id).style.display = "none";
txtName.style.display = "";
txtName.value = name;
txtName.focus();
var txtGenre = document.getElementById("txtGenre_" + id);
document.getElementById("spnGenre_" + id).style.display = "none";
txtGenre.style.display = "";
txtGenre.value = genre;
document.getElementById("btnEdit_" + id).style.display = "none";
document.getElementById("btnUpdate_" + id).style.display = "";
document.getElementById("btnCancel_" + id).style.display = "";
}
else {
document.getElementById("spnName_" + id).style.display = "";
document.getElementById("txtName_" + id).style.display = "none";
document.getElementById("spnGenre_" + id).style.display = "";
document.getElementById("txtGenre_" + id).style.display = "none";
document.getElementById("btnEdit_" + id).style.display = "";
document.getElementById("btnUpdate_" + id).style.display = "none";
document.getElementById("btnCancel_" + id).style.display = "none";
}
}
};
</script>
toggleUIView
函数包含用于在 UI 中切换编辑、更新和取消按钮的逻辑。
有关 Blazor 的更多信息,请参阅 此链接。
运行 Blazor 应用
在开始测试之前,请确保运行命令 docker ps
以确认所有必需的容器都已启动并正在运行,如下图所示
如你所见,blazorapp
、band.api
和 band.data
Docker 容器都已启动并正在运行。现在我们可以安全地测试应用程序了。
现在,打开你的 BlazorApp
解决方案项目所在位置的终端控制台,然后执行以下操作
dotnet build
dotnet run
这是我的 Mac 上 Blazor 应用在 Docker 容器中运行时屏幕截图
就是这样!目标已达成!太棒了!
GitHub 仓库
摘要
在本文中,我们学到了很多东西,从设置开发环境、从头开始创建 REST API 和数据驱动的 SPA 应用,到将 ASP.NET Core Web API 和 Blazor 应用部署到 Docker 容器。总结一下,我们学到了以下内容
- 我们想要实现的目标
- 设置开发环境
- 配置用于 Linux 的 SQL Server 数据库
- 使用 Valentina Studio 管理数据库
- 创建 ASP.NET Core Web API 应用程序
- Docker 化 Web API 应用程序
- 创建你的第一个 Blazor 应用程序
- 将所有应用程序从 Docker 容器连接起来
抱歉文章太长。但我希望你从这篇文章中学到了一些东西。
参考文献
- https://docs.microsoft.com/en-us/sql/linux/quickstart-install-connect-docker?view=sql-server-2017
- https://docs.dockerd.com.cn/engine/reference/commandline/docker/
- https://valentina-db.com/dokuwiki/doku.php?id=valentina:products:vstudio:vstudio
- https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/docker/visual-studio-tools-for-docker?view=aspnetcore-2.1
- https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/multi-container-microservice-net-applications/multi-container-applications-docker-compose
历史
- 2018 年 10 月 6 日:初始版本