教學:將 ASP.NET Core 應用程式連線到 .NET Aspire 存儲整合
雲端原生應用程式通常需要可調整的記憶體解決方案,以提供 Blob 記憶體、佇列或半結構化 NoSQL 資料庫等功能。 .NET Aspire 整合可簡化與各種儲存服務的連線,例如 Azure Blob Storage。 在本教學課程中,您將建立一個 ASP.NET Core 應用程式,該應用程式會使用 .NET Aspire 整合來連線至 Azure Blob Storage 和 Azure 佇列儲存體以提交支援票證。 應用程式會將票證傳送至佇列進行處理,並將附件上傳至記憶體。 您將瞭解如何:
- 建立已設定為使用 .NET Aspire 整合的基本 .NET 應用程式
- 新增 .NET.NET Aspire 整合以連接到多個儲存服務
- 設定及使用 .NET.NET Aspire 元件功能來傳送和接收數據
先決條件
若要使用 .NET.NET Aspire,您需要在本機安裝下列專案:
- .NET 8.0 或 .NET 9.0
- 符合 OCI 規範的容器運行時間,例如:
- Docker Desktop 或 Podman。 如需詳細資訊,請參閱 容器運行時間。
- 整合式開發人員環境 (IDE) 或程式碼編輯器,例如:
- Visual Studio 2022 17.9 版或更高版本 (選用)
-
Visual Studio Code (選擇性)
- C# Dev Kit:擴充功能(選擇性)
- JetBrains Rider 搭配 .NET.NET Aspire 外掛程式 (選用)
如需詳細資訊,請參閱 .NET.NET Aspire 設定和工具和 .NET.NET Aspire SDK。
探索已完成的範例應用程式
本教學課程中已完成的範例應用程式版本可在 GitHub取得。 專案也會結構化為 Azure Developer CLI的範本,這表示如果您已安裝工具 ,您可以使用 azd up
命令將 Azure 資源布建自動化。
git clone https://github.com/Azure-Samples/dotnet-aspire-connect-storage.git
設定 Azure 儲存資源
在本文中,您將需要具有 Blob 容器和記憶體佇列之 Azure 記憶體帳戶的數據參與者存取權。 請確定您有下列可用資源和組態:
在本文中,您必須使用模擬器在本機開發環境中建立 Blob 容器和記憶體佇列資源。 若要這樣做,請使用 Azurite。 Azurite 是可在 Docker 容器中執行的免費開放原始碼跨平臺 Azure 記憶體 API 相容 server(模擬器)。
若要使用模擬器,您必須 安裝 Azurite。
- Azure 記憶體帳戶 - 建立記憶體帳戶。
- 名為 fileuploads 的 Blob 儲存體容器, - 建立 Blob 儲存體容器。
- 名為 票證的記憶體佇列, - 建立記憶體佇列。
在 Azure CLI 或 CloudShell 中執行下列命令,以設定所需的 Azure 儲存器資源:
az group create --name aspirestorage --location eastus2
az storage account create -n aspirestorage -g aspirestorage -l eastus2
az storage container create -n fileuploads --account-name aspirestorage
az storage queue create -n tickets --account-name aspirestorage
您也需要將下列角色指派給登入 Visual Studio 的用戶帳戶:
- 儲存體 Blob 資料貢獻者 - 指派 Azure RBAC 角色
- 儲存體佇列資料貢獻者 - 指派 Azure RBAC 角色
Azure Developer CLI 可讓您使用範本系統布建和部署 Azure 資源。 本教學課程提供 完整的範本,可布建必要的 Azure 資源,並包含已完成的應用程式程式碼範例。 執行下列命令來初始化並執行範本:
執行
azd auth login
以登入 Azure:azd auth login
執行
azd init
來複製和初始化範例範本:azd init --template dotnet-aspire-connect-storage
執行
azd up
來佈建 Azure 資源:azd up
出現提示時,請選取已布建資源的訂用帳戶和 Azure 區域。 樣本會為您執行並完成下列工作:
- 建立具有已啟用 Blob 和佇列服務的 Azure 儲存帳戶
- 建立名為
fileUploads
的 Blob 記憶體容器 - 建立名為
tickets
的佇列 - 將下列角色指派給執行範本的用戶帳戶。
- 儲存體 Blob 資料貢獻者
- 儲存體佇列資料貢獻者
作業順利完成之後,您有兩個選項繼續進行:
- 選項 1:在範本
src
目錄中執行 .NET 範例應用程式,以實驗已完成的應用程式。 - 選項 2:使用前幾節逐步建置範例應用程式,並將它連線至
azd
所布建的 Azure 資源。
建立範例解決方案
使用 Visual Studio 或 .NET CLI 建立 .NET Aspire 專案。
- 在 Visual Studio的頂端,瀏覽至 [檔案]>[新增>專案]。
- 在對話框視窗中,搜尋 [Aspire],然後選取 [.NET.NET Aspire 入門應用程式]。 選擇 下一步。
- 在 [設定新專案 畫面:
- 輸入 [方案名稱]AspireStorage,然後選取 [下一步]。
- 在 附加資訊 畫面上:
- 取消選取 選項,使用 Redis 用於快取(在本教學中不需要)。
- 選取 Create。
Visual Studio 會創建一個使用 .NET Aspire的新 ASP.NET Core 解決方案。
解決方案包含以下專案:
- AspireStorage.ApiService - 具有預設 .NET.NET Aspire 服務組態的 API 專案。
- AspireStorage.AppHost - 協調器專案,其設計目的是連線及設定應用程式的不同項目和服務。 協調器應該設定為啟始專案。
- AspireStorage.ServiceDefaults - 共用類別庫,用來保存可在解決方案中跨專案重複使用的程式代碼。
- AspireStorage.Web - 做為應用程式前端的 BlazorServer 專案。
新增 Worker Service 專案
接下來,將 Worker Service 專案新增至方案,以擷取和處理訊息,因為它們會新增至 Azure 記憶體佇列。
- 在方案總管中,以滑鼠右鍵按兩下最上層 AspireStorage 方案節點,然後選取 [新增>新增專案]。
- 搜尋並選取 Worker Service 樣本,然後選擇 [下一步]。
- 針對 [項目名稱]輸入 AspireStorage.WorkerService,然後選取 下一步 。
- 在 [其他資訊 畫面上:
- 確定已選取 .NET 9.0。
- 確保已選取
登記於 ,然後選擇 [創建]協調流程 。
Visual Studio 將專案新增至您的方案,並使用新的程式代碼行更新 AspireStorage.AppHost 專案的 Program.cs 檔案:
builder.AddProject<Projects.AspireStorage_WorkerService>(
"aspirestorage-workerservice");
Visual Studio 工具新增這一行程式代碼,以向 IDistributedApplicationBuilder 對象註冊新專案,以啟用協調流程功能。 如需詳細資訊,請參閱 .NET.NET Aspire 協調流程概觀。
已完成的解決方案結構應該如下所示:
將 .NET Aspire 整合新增至 Blazor 應用程式
將 .NET AspireAzure Blob Storage 整合 和 .NET AspireAzure 佇列記憶體整合 套件新增至 AspireStorage.Web 專案:
dotnet add package Aspire.Azure.Storage.Blobs
dotnet add package Aspire.Azure.Storage.Queues
您的 AspireStorage.Web 項目現在已設定為使用 .NET.NET Aspire 整合。 以下是 aspireStorage.Web.csproj 檔案的更新
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\AspireStorage.ServiceDefaults\AspireStorage.ServiceDefaults.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Aspire.Azure.Storage.Blobs" Version="9.0.0" />
<PackageReference Include="Aspire.Azure.Storage.Queues" Version="9.0.0" />
</ItemGroup>
</Project>
下一個步驟是將整合新增至應用程式。
在 AspireStorage.Web 專案的 Program.cs 檔案中,於建立 builder
之後,但在呼叫 AddServiceDefaults
之前,新增對 AddAzureBlobClient 和 AddAzureQueueClient 擴充方法的呼叫。 如需詳細資訊,請參閱 .NET.NET Aspire 服務預設值。 提供連接字串的名稱做為參數。
using AspireStorage.Web;
using AspireStorage.Web.Components;
using Azure.Storage.Blobs;
using Azure.Storage.Queues;
var builder = WebApplication.CreateBuilder(args);
builder.AddAzureBlobClient("BlobConnection");
builder.AddAzureQueueClient("QueueConnection");
// Add service defaults & Aspire components.
builder.AddServiceDefaults();
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddOutputCache();
builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
// This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
// Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
client.BaseAddress = new("https+http://apiservice");
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
else
{
// In development, create the blob container and queue if they don't exist.
var blobService = app.Services.GetRequiredService<BlobServiceClient>();
var docsContainer = blobService.GetBlobContainerClient("fileuploads");
await docsContainer.CreateIfNotExistsAsync();
var queueService = app.Services.GetRequiredService<QueueServiceClient>();
var queueClient = queueService.GetQueueClient("tickets");
await queueClient.CreateIfNotExistsAsync();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.UseOutputCache();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.MapDefaultEndpoints();
app.Run();
using AspireStorage.Web;
using AspireStorage.Web.Components;
using Azure.Storage.Blobs;
using Azure.Storage.Queues;
var builder = WebApplication.CreateBuilder(args);
builder.AddAzureBlobClient("BlobConnection");
builder.AddAzureQueueClient("QueueConnection");
// Add service defaults & Aspire components.
builder.AddServiceDefaults();
// Add services to the container.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
builder.Services.AddOutputCache();
builder.Services.AddHttpClient<WeatherApiClient>(client =>
{
// This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
// Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
client.BaseAddress = new("https+http://apiservice");
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAntiforgery();
app.UseOutputCache();
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.MapDefaultEndpoints();
app.Run();
透過其他 using
語句,這些方法會完成下列工作:
- 向 DI 容器註冊 Azure.Storage.Blobs.BlobServiceClient 和 Azure.Storage.Queues.QueueServiceClient,以連線到 Azure 記憶體。
- 自動為個別服務啟用對應的健康情況檢查、記錄和遙測。
AspireStorage.Web 專案啟動時,將會在 Azurite Blob 儲存體中建立一個 fileuploads
容器,並在 Azurite Queue 儲存體中建立一個 tickets
佇列。 當應用程式在開發環境中執行時,這是有條件的。 當應用程式在生產環境中執行時,系統會假設已建立容器和佇列。
將 .NET Aspire 整合至 Worker Service
工人服務負責從 Azure 儲存佇列中提取訊息進行處理。 將 .NET AspireAzure 佇列記憶體整合 整合套件新增至 AspireStorage.WorkerService 應用程式:
dotnet add package Aspire.Azure.Storage.Queues
在 AspireStorage.WorkerService 專案的 Program.cs 檔案中,在建立 builder
之後且在呼叫 AddServiceDefaults
之前,新增對 AddAzureQueueClient 擴充方法的呼叫:
using AspireStorage.WorkerService;
var builder = Host.CreateApplicationBuilder(args);
builder.AddAzureQueueClient("QueueConnection");
builder.AddServiceDefaults();
builder.Services.AddHostedService<WorkerService>();
var host = builder.Build();
host.Run();
此方法會處理下列工作:
- 向 DI 容器註冊 QueueServiceClient,以連線到 Azure Storage Queues。
- 自動為個別服務啟用對應的健康情況檢查、記錄和遙測。
建立表單
應用程式需要表單,使用者才能提交支援票證資訊並上傳附件。 應用程式會使用插入的 BlobServiceClient,將 Document
(IFormFile) 屬性上的附加檔案上傳至 Azure Blob Storage。
QueueServiceClient 會將由 Title
和 Description
組成的訊息傳送至 Azure 記憶體佇列。
使用下列 Razor 標記來建立基本表單,以取代 AspireStorage.Web/Components/Pages 目錄中 Home.razor 檔案的內容:
@page "/"
@using System.ComponentModel.DataAnnotations
@using Azure.Storage.Blobs
@using Azure.Storage.Queues
@inject BlobServiceClient BlobClient
@inject QueueServiceClient QueueServiceClient
<PageTitle>Home</PageTitle>
<div class="text-center">
<h1 class="display-4">Request Support</h1>
</div>
<EditForm Model="@Ticket" FormName="Tickets" method="post"
OnValidSubmit="@HandleValidSubmit" enctype="multipart/form-data">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="mb-4">
<label>Issue Title</label>
<InputText class="form-control" @bind-Value="@Ticket.Title" />
<ValidationMessage For="() => Ticket.Title" />
</div>
<div class="mb-4">
<label>Issue Description</label>
<InputText class="form-control" @bind-Value="@Ticket.Description" />
<ValidationMessage For="() => Ticket.Description" />
</div>
<div class="mb-4">
<label>Attachment</label>
<InputFile class="form-control" name="Ticket.Document" />
<ValidationMessage For="() => Ticket.Document" />
</div>
<button class="btn btn-primary" type="submit">Submit</button>
<button class="btn btn-danger mx-2" type="reset" @onclick=@ClearForm>Clear</button>
</EditForm>
@code {
[SupplyParameterFromForm(FormName = "Tickets")]
private SupportTicket Ticket { get; set; } = new();
private async Task HandleValidSubmit()
{
var docsContainer = BlobClient.GetBlobContainerClient("fileuploads");
// Upload file to blob storage
await docsContainer.UploadBlobAsync(
Ticket.Document.FileName,
Ticket.Document.OpenReadStream());
// Send message to queue
var queueClient = QueueServiceClient.GetQueueClient("tickets");
await queueClient.SendMessageAsync(
$"{Ticket.Title} - {Ticket.Description}");
ClearForm();
}
private void ClearForm() => Ticket = new();
private class SupportTicket()
{
[Required] public string Title { get; set; } = default!;
[Required] public string Description { get; set; } = default!;
[Required] public IFormFile Document { get; set; } = default!;
}
}
如需在 Blazor中建立表單的詳細資訊,請參閱 ASP.NET CoreBlazor 表單概觀。
更新 AppHost
AspireStorage.AppHost 項目是應用程式的協調器。 它負責連線和設定應用程式的不同項目和服務。 協調器應該設定為啟始專案。
若要將 Azure 儲存裝載支援新增至您的 IDistributedApplicationBuilder,請安裝 📦AspireHostingAzureStorage NuGet 套件。
dotnet add package Aspire.Hosting.Azure.Storage
以下列程式代碼取代 AspireStorage.AppHost 專案中 Program.cs 檔案的內容:
using Microsoft.Extensions.Hosting;
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureStorage("Storage");
if (builder.Environment.IsDevelopment())
{
storage.RunAsEmulator();
}
var blobs = storage.AddBlobs("BlobConnection");
var queues = storage.AddQueues("QueueConnection");
var apiService = builder.AddProject<Projects.AspireStorage_ApiService>("apiservice");
builder.AddProject<Projects.AspireStorage_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(apiService)
.WithReference(blobs)
.WithReference(queues);
builder.AddProject<Projects.AspireStorage_WorkerService>("aspirestorage-workerservice")
.WithReference(queues);
builder.Build().Run();
上述程式代碼會新增 Azure 儲存體、Blob 和佇列,且在開發模式下會使用模擬器。 每個項目都會定義他們所依賴資源的參考。
using Microsoft.Extensions.Hosting;
var builder = DistributedApplication.CreateBuilder(args);
var storage = builder.AddAzureStorage("Storage");
var blobs = storage.AddBlobs("BlobConnection");
var queues = storage.AddQueues("QueueConnection");
var apiService = builder.AddProject<Projects.AspireStorage_ApiService>("apiservice");
builder.AddProject<Projects.AspireStorage_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(apiService)
.WithReference(blobs)
.WithReference(queues);
builder.AddProject<Projects.AspireStorage_WorkerService>("aspirestorage-workerservice")
.WithReference(queues);
builder.Build().Run();
前面的程式碼會新增 Azure 儲存、Blob 和佇列,並在每個相依於這些資源的專案內定義它們的參考。
處理佇列中的項目
當新的訊息放置在 tickets
佇列時,工作者服務應該擷取、處理和刪除該訊息。 更新 Worker.cs 類別,以下列程式代碼取代內容:
using Azure.Storage.Queues;
using Azure.Storage.Queues.Models;
namespace AspireStorage.WorkerService;
public sealed class WorkerService(
QueueServiceClient client,
ILogger<WorkerService> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var queueClient = client.GetQueueClient("tickets");
await queueClient.CreateIfNotExistsAsync(cancellationToken: stoppingToken);
while (!stoppingToken.IsCancellationRequested)
{
QueueMessage[] messages =
await queueClient.ReceiveMessagesAsync(
maxMessages: 25, cancellationToken: stoppingToken);
foreach (var message in messages)
{
logger.LogInformation(
"Message from queue: {Message}", message.MessageText);
await queueClient.DeleteMessageAsync(
message.MessageId,
message.PopReceipt,
cancellationToken: stoppingToken);
}
// TODO: Determine an appropriate time to wait
// before checking for more messages.
await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
}
}
}
在背景工作服務處理訊息之前,需要先能夠連線到 Azure 儲存體佇列。 在使用 Azurite 時,您必須確保佇列在工作者服務開始進行訊息佇列處理之前是可用的。
using Azure.Storage.Queues;
using Azure.Storage.Queues.Models;
namespace AspireStorage.WorkerService;
public sealed class WorkerService(
QueueServiceClient client,
ILogger<WorkerService> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var queueClient = client.GetQueueClient("tickets");
while (!stoppingToken.IsCancellationRequested)
{
QueueMessage[] messages =
await queueClient.ReceiveMessagesAsync(
maxMessages: 25, cancellationToken: stoppingToken);
foreach (var message in messages)
{
logger.LogInformation(
"Message from queue: {Message}", message.MessageText);
await queueClient.DeleteMessageAsync(
message.MessageId,
message.PopReceipt,
cancellationToken: stoppingToken);
}
// TODO: Determine an appropriate time to wait
// before checking for more messages.
await Task.Delay(TimeSpan.FromSeconds(15), stoppingToken);
}
}
}
工作服務會藉由連線到 Azure 儲存佇列,並從佇列中取出訊息來處理訊息。
背景工作服務會在佇列中處理訊息,並在處理後將其刪除。
設定連接字串
AspireStorage 和 AspireStorage.Worker 項目必須設定為連線到您稍早建立的正確 Azure 記憶體帳戶。 您可以使用每個專案中的 appsettings.json 檔案,在記憶體帳戶中指定 Blob 和佇列服務的端點。
在 AspireStorage 專案中,將下列組態新增至
appsettings.Development.json
檔案:"ConnectionStrings": { "BlobConnection": "https://<your-storage-account-name>.blob.core.windows.net/", "QueueConnection": "https://<your-storage-account-name>.queue.core.windows.net/" }
在 AspireStorage.Worker 專案中,將下列組態新增至
appsettings.Development.json
檔案:"ConnectionStrings": { "QueueConnection": "https://<your-storage-account-name>.queue.core.windows.net/" }
在本機執行及測試應用程式
範例應用程式現在已準備好進行測試。 完成下列步驟以確認提交的表單數據已傳送至 Azure Blob Storage 和 Azure 佇列儲存:
按下 Visual Studio 頂端的 [執行] 按鈕,在瀏覽器中啟動您的 .NET Aspire 項目儀錶板。
在 [資源] 頁面上,在 [aspirestorage.web] 數據列中,按兩下 [端點] 數據行中的連結,以開啟應用程式的 UI。
在
Title
和Description
表單域中輸入範例數據,然後選取要上傳的簡單檔案。選取 [提交] 按鈕,表單會提交支援票證進行處理,並清除表單內容。
在個別的瀏覽器索引標籤中,使用 Azure 入口網站導覽至您的 Azure 儲存帳戶中的 儲存瀏覽器。
選取 [容器],然後流覽至 [檔] 容器,以查看上傳的檔案。
您可以查看 .NET.NET Aspire 儀錶板的 Project 記錄,以確認佇列上的訊息已處理,然後從下拉式清單中選取 aspirestorage.workerservice。
總結
您建置的範例應用程式示範如何從 ASP.NET CoreBlazor Web 應用程式保存 Blob,並在 .NET Worker Service中處理佇列。 您的應用程式會使用 .NET Aspire 整合來連線到 Azure 儲存。 應用程式會將支援票證傳送至佇列進行處理,並將附件上傳至記憶體。
由於您選擇使用 Azurite,因此當您完成測試時,沒有必要清除這些資源,因為您在模擬器環境中於本機創建了這些資源。 模擬器可讓您在本機測試應用程式,而不會產生任何成本,因為未布建或建立任何 Azure 資源。
清除資源
當您不再需要您所建立 Azure 資源時,請執行下列 Azure CLI 命令來刪除資源群組。 刪除資源群組也會刪除其內含的資源。
az group delete --name <your-resource-group-name>
如需詳細資訊,請參閱