练习 - 使用数据库服务来保存 .NET Aspire 项目中的数据
在本练习中,你将替换公司正在开发的云原生应用程序的当前数据存储。 目前,该应用程序使用本地存储的 SQLite 数据库来存储目录数据,并使用内存中的 Redis 缓存来存储客户的购物车。 将现有数据存储替换为 PostgreSQL 和 MongoDB。
安装先决条件
.NET Aspire 的先决条件包括:
- .NET 8
- Visual Studio 2022 预览版
- Docker Desktop 或 Podman
- Visual Studio 中的 .NET Aspire 工作负载
如果已安装必备组件,则可以跳到克隆现有应用。
安装 .NET 8
单击此 .NET 8 链接,然后根据操作系统选择正确的安装程序。 例如,如果使用的是 Windows 11 和新式处理器,请选择适用于 Windows 的 x64 .NET 8 SDK。
下载完成后,运行安装程序并按照说明操作。 在终端窗口中,运行以下命令以验证安装是否成功:
dotnet --version
应会看到所安装的 .NET SDK 版本号。 例如:
8.0.300-preview.24203.14
安装 Visual Studio 2022 预览版
单击此 Visual Studio 2022 预览版链接,然后选择“下载预览版”。 下载完成后,运行安装程序并按照说明操作。
安装 Docker Desktop
单击此 Docker Desktop 链接,然后根据操作系统选择正确的安装程序。 下载完成后,运行安装程序并按照说明操作。
打开 Docker Desktop 应用程序并接受服务协议。
在 Visual Studio 中安装 .NET Aspire 工作负载
使用 .NET CLI 安装 .NET Aspire 工作负载:
打开终端。
使用以下命令安装 .NET Aspire 工作负载:
dotnet workload update dotnet workload install aspire dotnet workload list
应会看到 .NET Aspire 工作负载的详细信息。
Installed Workload Id Manifest Version Installation Source --------------------------------------------------------------------------------------------- aspire 8.0.0/8.0.100 SDK 8.0.300-preview.24203, VS 17.10.34902.84 Use `dotnet workload search` to find additional workloads to install.
克隆和修改 Northern Mountains 应用
让我们使用 git
获取当前的 Northern Mountains 应用:
在命令行中,浏览到你选择的、可在其中处理代码的文件夹。
执行以下命令克隆 Northern Mountains eShop 示例应用程序:
git clone -b aspire-databases https://github.com/MicrosoftDocs/mslearn-aspire-starter
启动 Visual Studio,然后选择“打开项目或解决方案”。
浏览到你克隆了 eShop 的文件夹,打开 start 文件夹并选择 eShop.databases.sln 文件,然后选择“打开”。
在“解决方案资源管理器”中,展开“eShop.AppHost”项目,然后打开“Program.cs”。
// Databases var basketStore = builder.AddRedis("BasketStore").WithRedisCommander(); // Identity Providers var idp = builder.AddKeycloakContainer("idp", tag: "23.0") .ImportRealms("../Keycloak/data/import"); // DB Manager Apps builder.AddProject<Projects.Catalog_Data_Manager>("catalog-db-mgr"); // API Apps var catalogApi = builder.AddProject<Projects.Catalog_API>("catalog-api"); var basketApi = builder.AddProject<Projects.Basket_API>("basket-api") .WithReference(basketStore) .WithReference(idp); // Apps // Force HTTPS profile for web app (required for OIDC operations) var webApp = builder.AddProject<Projects.WebApp>("webapp") .WithReference(catalogApi) .WithReference(basketApi) .WithReference(idp, env: "Identity__ClientSecret");
上面的代码显示了应用程序的当前配置。 该应用使用 Redis 缓存来存储购物车。
浏览应用的其余部分,重点关注 Catalog.Data.Manager 和 Catalog.API 项目,并了解它们如何使用本地存储的 SQLite 数据库。
若要启动应用,请按 F5 或选择“调试 > 开始调试”。
如果出现“启动 Docker Desktop”对话框,请选择“是”。
出现 eShop .NET Aspire 仪表板时,对于 webapp 资源,请选择安全终结点:
应用将在浏览器中打开。 可以浏览应用并查看其工作原理。
测试用户凭据为 test@example.com 和 P@$$w0rd1。
若要停止调试,请按 Shift+F5,或选择“调试 > 停止调试”。
添加 .NET Aspire PostgreSQL 组件
负责目录微服务的团队构建了该应用程序以使用本地存储的 SQLite 数据库。 此方法适用于开发,但团队希望使用更可靠的数据库进行生产。
有两个项目连接到 SQLite 数据库,即 Catalog.Data.Manager 和 Catalog.API 项目。 数据管理器仅用于向数据库添加数据,因此应该重点关注 Catalog.API 项目。
在“解决方案资源管理器”中,右键单击“Catalog.API”项目,然后选择“添加”>“.NET Aspire 包”。
在“搜索”框中,将 Npgsql.EntityFramework 添加到末尾,然后按 Enter。
在左侧的结果中,选择“Aspire.Npgsql.EntityFrameworkCore.PostgreSQL”。
在右侧,选择版本下拉菜单,然后选择最新的“8.0.0”版本。
选择“安装” 。
如果出现“预览更改”对话框,请选择“应用”。
在“许可接受”对话框中,选择“我接受”。
在“解决方案资源管理器”中,选择“Catalog.API”项目以查看 Catalog.API.csproj 文件的内容。
删除 Microsoft.EntityFrameworkCore.Sqlite 的
PackageReference
:<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.3" />
注册新的 PostgreSQL DbContext
在“解决方案资源管理器”中,展开“Catalog.API”项目,然后打开“Program.cs”文件。
替换 SQLite DbContext:
builder.Services.AddDbContext<CatalogDbContext>( options => options.UseSqlite(builder.Configuration.GetConnectionString("sqlconnection") ?? throw new InvalidOperationException( "Connection string 'sqlconnection' not found.")));
使用新的 PostgreSQL DbContext:
builder.AddNpgsqlDbContext<CatalogDbContext>("CatalogDB");
该应用不再需要读取 Database.db 文件,因此请删除 appsettings.json 中的关联字符串。
在“解决方案资源管理器”的“catalog.API”下,选择“appsettings.json”。
删除
ConnectionStrings
条目,该文件现在将如下所示:{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "OpenApi": { "Endpoint": { "Name": "Catalog.API v1" }, "Document": { "Description": "The Catalog Microservice HTTP API. This is a Data-Driven/CRUD microservice sample", "Title": "eShop - Catalog HTTP API", "Version": "v1" } }, "CatalogOptions": { "PicBasePathFormat": "items/{0}/pic/" } }
右键单击“Catalog.Data.Manager”项目,然后选择“删除”。
在对话框中选择“确定”。
数据库团队会创建 PostgreSQL 数据库备份,用于创建目录数据库并为其设定种子。 可以在“Catalog.API/Seed”文件夹中查看备份。
使用绑定卷为 PostgreSQL 数据库设定种子
AppHost 项目可以创建一个 PostgreSQL 数据库容器,使用来自绑定卷的数据为其设定种子,然后通过依赖项注入将引用传递到 Catalog.API。
在“解决方案资源管理器”中,右键单击“eShop.AppHost”项目,然后选择“添加”>“.NET Aspire 包”。
在“搜索”框中,将 PostgreSQL 添加到末尾,然后按 Enter。
在左侧的结果中选择“Aspire.Hosting.PostgreSQL”。
在右侧,选择版本下拉菜单,然后选择最新的“8.0.0”版本。
选择“安装” 。
如果出现“预览更改”对话框,请选择“应用”。
在“许可接受”对话框中,选择“我接受”。
在“解决方案资源管理器”中,展开“eShop.AppHost”项目,然后打开“Program.cs”文件。
在
//Databases
注释下,添加以下代码:// Databases var basketStore = builder.AddRedis("BasketStore").WithRedisCommander(); var postgres = builder.AddPostgres("postgres") .WithEnvironment("POSTGRES_DB", "CatalogDB") .WithBindMount("../Catalog.API/Seed", "/docker-entrypoint-initdb.d").WithPgAdmin(); var catalogDB = postgres.AddDatabase("CatalogDB");
上述代码将创建一个 PostgreSQL 数据库容器,添加一个名为 CatalogDB 的数据库,并将 /docker-entrypoint-initdb.d 目录绑定到 ../Catalog.API/Seed 目录。 该代码还会为 pgAdmin 工具创建一个容器,用于管理 PostgreSQL 数据库。
通过添加
.WithReference(catalogDB)
将catalogDB
引用传递给 Catalog.API 项目,代码现在如下所示:// API Apps var catalogApi = builder.AddProject<Projects.Catalog_API>("catalog-api") .WithReference(catalogDB);
不再需要 Catalog.Data.Manager 项目,因此请将其从 AppHost 中删除。 删除此代码:
// DB Manager Apps builder.AddProject<Projects.Catalog_Data_Manager>("catalog-db-mgr");
测试应用程序
使用 .NET Aspire 允许团队删除整个项目。 此外,目录 API 只需要一行代码即可添加 PostgresQL 数据库上下文。 来自 AppHost 的依赖项注入和服务发现意味着不需要其他代码更改即可允许 API 连接到新的数据库。
编译并启动应用,按 F5 或选择“调试 > 开始调试”。
仪表板中有两个新容器,用于托管 PostgreSQL 数据库服务器和 pgAdmin 工具。 还有一个用于托管 CatalogDB 数据库的 PostgreSQL 数据库资源。
使用 pgAdmin 连接到 PostgreSQL 数据库并浏览数据。 选择 postgres pgadmin 终结点。
展开“aspire 实例”>“postgres”>“数据库”>“CatalogDB”>“架构”>“目录”>“表”。 然后右键单击“目录”表,然后选择“查看/编辑数据”>“前 100 行”。
可以看到 AppHost 加载的数据。
在浏览器中选择“eShop 资源”仪表板选项卡,然后选择 webapp 终结点。
应用将打开并像往常一样工作。
若要停止调试,请按 Shift+F5,或选择“调试 > 停止调试”。
将 .NET Aspire MongoDB 组件添加到应用
当前应用使用 Redis 作为客户购物车的内存数据存储。 团队希望为购物车使用更可靠且持久的数据存储。 将 Redis 缓存替换为 MongoDB 数据库。
将 Basket.API 更改为使用 MongoDB
- 在“解决方案资源管理器”中,右键单击“Basket.API”项目,选择“添加”,然后选择“添加”>“.NET Aspire 包”。
- 在“搜索”框中,在末尾输入 MongoDB,然后按 Enter。
- 选择“Aspire.MongoDB.Driver”,然后选择最新的“8.0.0”版本。
- 选择“安装” 。
- 如果出现“预览更改”对话框,请选择“应用”。
- 在“许可接受”对话框中,选择“我接受”。@
创建 MongoDB 购物车存储
购物车微服务使用 HostingExtensions
来管理 Redis 数据存储。 将 Redis 数据存储替换为 MongoDB 数据存储。
在“解决方案资源管理器”中,展开“Basket.API”项目,选择“存储”文件夹,然后选择“RedisBasketStore.cs”文件。
有两种异步方法(
GetBasketAsync
和UpdateBasketAsync
)使用 Redis 缓存。 让我们来创建这些方法的 MongoDB 版本。在“解决方案资源管理器”中,右键单击“存储”文件夹,然后选择“添加”>“类”。
在“添加新项”对话框中,将文件命名为“MongoBasketStore.cs”,然后选择“添加”。
将 MongoBasketStore.cs 文件中的代码替换为以下代码:
using eShop.Basket.API.Models; using MongoDB.Driver; using MongoDB.Driver.Linq; namespace eShop.Basket.API.Storage; public class MongoBasketStore { private readonly IMongoCollection<CustomerBasket> _basketCollection; public MongoBasketStore(IMongoClient mongoClient) { // The database name needs to match the created database in the AppHost _basketCollection = mongoClient.GetDatabase("BasketDB").GetCollection<CustomerBasket>("basketitems"); } public async Task<CustomerBasket?> GetBasketAsync(string customerId) { var filter = Builders<CustomerBasket>.Filter.Eq(r => r.BuyerId, customerId); return await _basketCollection.Find(filter).FirstOrDefaultAsync(); } public async Task<CustomerBasket?> UpdateBasketAsync(CustomerBasket basket) { var filter = Builders<CustomerBasket>.Filter.Eq(r => r.BuyerId, basket.BuyerId); var result = await _basketCollection.ReplaceOneAsync(filter, basket, new ReplaceOptions { IsUpsert = true }); return result.IsModifiedCountAvailable ? basket : null; } }
上述代码将创建一个
MongoBasketStore
类,该类适用于CustomerBasket
模型。 该集合处理针对 MongoDB 数据库中客户购物车的 CRUD 操作。在“解决方案资源管理器”中,展开“Basket.API”>“扩展”,然后选择“HostingExtensions.cs”文件。
替换 Redis 代码:
builder.AddRedis("BasketStore"); builder.Services.AddSingleton<RedisBasketStore>();
使用 MongoDB 代码:
builder.AddMongoDBClient("BasketDB"); builder.Services.AddSingleton<MongoBasketStore>();
在“解决方案资源管理器”中,展开“Grpc”文件夹,然后打开“BasketService.cs”文件。
更改类以接受
MongoBasketStore
,替换:public class BasketService(RedisBasketStore basketStore) : Basket.BasketBase
替换为:
public class BasketService(MongoBasketStore basketStore) : Basket.BasketBase
将 MongoDB 数据库添加到 AppHost
在“解决方案资源管理器”中,右键单击“eShop.AppHost”项目,然后选择“添加”>“.NET Aspire 包”。
在“搜索”框中,在末尾输入 MongoDB,然后按 Enter。
选择“ Aspire.Hosting.MongoDB”,然后选择最新的“8.0.0”版本。
选择“安装” 。
如果出现“预览更改”对话框,请选择“应用”。
在“许可接受”对话框中,选择“我接受”。@
在“解决方案资源管理器”中,展开“eShop.AppHost”项目,然后打开“Program.cs”文件。
在“数据库”部分中,添加 MongoDB 组件:
var mongo = builder.AddMongoDB("mongo") .WithMongoExpress() .AddDatabase("BasketDB");
上述代码将创建一个 MongoDB 数据库容器,并添加名为 BasketDB的数据库。 该代码还会为 Mongo Express 工具创建一个容器,用于管理 MongoDB 数据库。
删除 Redis 容器:
var basketStore = builder.AddRedis("BasketStore").WithRedisCommander();
代码现在应如下所示:
// Databases var postgres = builder.AddPostgres("postgres") .WithEnvironment("POSTGRES_DB", "CatalogDB") .WithBindMount("../Catalog.API/Seed", "/docker-entrypoint-initdb.d") .WithPgAdmin(); var catalogDB = postgres.AddDatabase("CatalogDB"); var mongo = builder.AddMongoDB("mongo") .WithMongoExpress() .AddDatabase("BasketDB");
Basket.API 项目需要对新的 MongoDB 数据库的引用,应该移除 Redis 引用:
var basketApi = builder.AddProject<Projects.Basket_API>("basket-api") .WithReference(mongo) .WithReference(idp);
Basket.API 项目现已准备好使用 MongoDB 数据库。 让我们测试应用,看看它是否有效。
测试应用程序
编译并启动应用,按 F5 或选择“调试 > 开始调试”。
可以在仪表板中看到新的 MongoDB 容器,一个用于数据库服务器,另一个用于 Mongo Express。 还有一个新的 MongoDBDatabase 资源,用于托管 BasketDB 数据库。
选择 webapp 终结点。
要使用测试用户凭据登录,请选择右上角的用户图标。 电子邮件地址是 test@example.com,密码是 P@$$w0rd1。
从主页中选择 Adventurer GPS Watch。
选择“添加到购物袋”,应该会看到一个异常:
调试应用
当你尝试向购物车添加项目时,应用将引发异常。 可以使用仪表板来帮助调试问题。
在浏览器中选择“eShop 资源”仪表板选项卡。
仪表板显示 basket-api 和 webapp 中存在错误。 查看 basket-api 的日志。
对于“basket-api”资源,在“日志”列中,选择“查看”。
有一个异常:
System.FormatException: Element '_id' does not match any field or property of class eShop.Basket.API.Models.CustomerBasket.
选择“资源”菜单项,然后选择 mongo-mongoexpress 终结点。
在“数据库”部分的 BasketDB 旁边,选择“查看”。
在“集合”中的“购物车项”旁边,选择“查看”。
存储在 MongoDB 中的文档具有“_id”字段。 存储在 MongoDB 集合中的每个文档都必须具有唯一的“_id”字段。
若要停止调试,请按 Shift+F5,或选择“调试 > 停止调试”。
查看代码并修复问题
让我们查看一下 CustomerBasket,看看能否找到问题。
在“解决方案资源管理器”中,展开“Basket.API”>“模型”文件夹,然后打开“CustomerBasket.cs”文件。
public class CustomerBasket { public required string BuyerId { get; set; } public List<BasketItem> Items { get; set; } = []; }
CustomerBasket 模型没有与“_id”字段匹配的字段或属性。 实体框架正在尝试将“_id”字段映射到 CustomerBasket 模型,但找不到匹配项。
更新
CustomerBasket
模型以包含“_id”字段:public class CustomerBasket { /// <summary> /// MongoDB document identifier /// </summary> public string _id { get; set; } = ""; public required string BuyerId { get; set; } public List<BasketItem> Items { get; set; } = []; }
测试固定应用
若要编译并启动应用,请按 F5 或选择“调试 > 开始调试”。
对于 webapp,在“终结点”列中,右键单击 URL,然后选择“在 InPrivate 窗口中打开链接”。
使用 InPrivate 窗口可确保浏览器不会使用以前的会话 Cookie 进行身份验证。
要使用测试用户凭据登录,请选择右上角的用户图标。 电子邮件地址是 test@example.com,密码是 P@$$w0rd1。
从主页中选择 Adventurer GPS Watch。
选择“添加到购物袋”。
Northern Mountains 应用购物车功能正在运行。
已成功将 SQLite 数据库替换为 PostgreSQL 数据库,并将 Redis 缓存替换为 MongoDB 数据库。 你使用 .NET Aspire 管理数据库并浏览其中的数据,并使用仪表板帮助调试应用的问题。