Tutorial: Conectar um aplicativo ASP.NET Core a integrações de armazenamento .NET Aspire
Os aplicativos nativos da nuvem geralmente exigem soluções de armazenamento escaláveis que fornecem recursos como armazenamento de blob, filas ou bancos de dados NoSQL semiestruturados. .NET Aspire integrações simplificam as conexões com vários serviços de armazenamento, como Azure Blob Storage. Neste tutorial, irá criar uma aplicação ASP.NET Core que usa integrações .NET Aspire para se conectar ao Armazenamento de Filas do Azure Blob Storage e Azure para enviar pedidos de suporte. O aplicativo envia os tíquetes para uma fila para processamento e carrega um anexo para o armazenamento. Você aprenderá a:
- Criar um aplicativo .NET básico configurado para usar integrações .NET Aspire
- Adicione integrações .NET.NET Aspire para se conectar a vários serviços de armazenamento
- Configurar e usar os recursos do .NET.NET Aspire Componente para enviar e receber dados
Pré-requisitos
Para trabalhar com .NET.NET Aspire, você precisa do seguinte instalado localmente:
- .NET 8,0 ou .NET 9,0
- Um ambiente de execução de contentor compatível com OCI, como:
- Docker Desktop ou Podman. Para obter mais informações, consulte Container runtime.
- Um ambiente de desenvolvedor integrado (IDE) ou editor de código, como:
- Visual Studio 2022 versão 17.9 ou superior (opcional)
-
Visual Studio Code (Opcional)
- C# Dev Kit: Extensão (Opcional)
- JetBrains Rider com .NET.NET Aspire plugin (Opcional)
Para obter mais informações, consulte .NET.NET Aspire configuração e ferramentas, e .NET.NET Aspire SDK.
Explore o aplicativo de exemplo concluído
Uma versão completa do aplicativo de exemplo deste tutorial está disponível em GitHub. O projeto também é estruturado como um modelo para o Azure Developer CLI, o que significa que você pode usar o comando azd up
para automatizar o provisionamento de recursos Azure se tiver a ferramenta instalada.
git clone https://github.com/Azure-Samples/dotnet-aspire-connect-storage.git
Configurar os recursos de armazenamento Azure
Para este artigo, você precisará de acesso de contribuidor de dados a uma conta de armazenamento Azure com um contêiner de blob e fila de armazenamento. Certifique-se de ter os seguintes recursos e configurações disponíveis:
Para este artigo, você precisará criar um contêiner de blob e um recurso de fila de armazenamento em seu ambiente de desenvolvimento local usando um emulador. Para fazer isso, use Azurite. O Azurite é um server (emulador) gratuito, de código aberto e compatível com a API de armazenamento Azure multiplataforma que é executado em um contêiner Docker.
Para usar o emulador, você precisa instalar o Azurite.
- Uma conta de armazenamento Azure - Crie uma conta de armazenamento.
- Um contêiner de Armazenamento de Blob chamado fileuploads - Criar um contêiner de armazenamento de blob.
- Uma fila de armazenamento nomeada - Criar uma fila de armazenamento.
Execute os seguintes comandos na CLI do Azure ou no CloudShell para configurar os recursos necessários do Azure Storage:
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
Você também precisa atribuir as seguintes funções à conta de usuário com a qual você está conectado em Visual Studio:
- Contribuidor de Dados de Blob de Armazenamento - Atribuir uma função RBAC Azure
- Contribuidor de Dados da Fila de Armazenamento - Atribuir uma função RBAC Azure
O Azure Developer CLI permite provisionar e implantar recursos Azure usando um sistema de modelos. Este tutorial fornece um modelo completo que provisiona os recursos de Azure necessários e inclui o código de aplicativo de exemplo concluído. Execute os seguintes comandos para inicializar e executar o modelo:
Execute
azd auth login
para iniciar sessão no Azure:azd auth login
Execute
azd init
para clonar e inicializar o modelo de exemplo:azd init --template dotnet-aspire-connect-storage
Execute
azd up
para provisionar os recursos Azure:azd up
Quando solicitado, selecione a assinatura e a região Azure para os recursos provisionados. O modelo é executado e conclui as seguintes tarefas para você:
- Cria uma conta de armazenamento Azure com os serviços de blob e fila ativados
- Cria um contêiner de armazenamento de blob chamado
fileUploads
- Cria uma fila chamada
tickets
- Atribui as seguintes funções à conta de usuário que executou o modelo.
- Contribuidor de dados de Blob de armazenamento
- Contribuidor de dados da fila de armazenamento
Depois que a operação for concluída com êxito, você terá duas opções para avançar:
- Opção 1: Execute o aplicativo de exemplo .NET no diretório de modelo
src
para experimentar o aplicativo concluído. - Opção 2: Crie o aplicativo de exemplo passo a passo usando as seções à frente e conecte-o aos recursos de Azure provisionados pelo
azd
.
Criar a solução de exemplo
Crie um projeto .NET Aspire usando o Visual Studio ou a CLI .NET.
- No topo de Visual Studio, vá para Arquivo>Novo>Projeto.
- Na janela de diálogo, procure por Aspire e selecione Aplicação Inicial .NET.NET Aspire. Escolha Próximo.
- Na tela Configurar seu novo projeto:
- Insira um Nome da Solução de AspireStorage e selecione Avançar.
- No ecrã de Informações adicionais
- Desmarque Usar Redis para de cache (não necessário para este tutorial).
- Selecione Criar.
Visual Studio cria uma nova solução ASP.NET Core que é estruturada para usar .NET Aspire.
A solução consiste nos seguintes projetos:
- AspireStorage.ApiService - Um projeto de API com configurações de serviço .NET.NET Aspire padrão.
- AspireStorage.AppHost - Um projeto orquestrador projetado para conectar e configurar os diferentes projetos e serviços do seu aplicativo. O orquestrador deve ser definido como o projeto de arranque.
- AspireStorage.ServiceDefaults - Uma biblioteca de classes compartilhada para armazenar código que pode ser reutilizado em todos os projetos em sua solução.
- AspireStorage.Web - Um projeto BlazorServer que serve como front-end do seu aplicativo.
Adicionar o projeto Worker Service
Em seguida, adicione um projeto Worker Service à solução para recuperar e processar mensagens à medida que são adicionadas à fila de armazenamento Azure.
- No Explorador de Soluções, clique com o botão direito do rato no nó de nível superior AspireStorage e selecione Adicionar>Novo Projeto.
- Procure e selecione o modelo Worker Service e escolha Avançar.
- Para o nome do projeto , digite AspireStorage.WorkerService e selecione Avançar.
- No ecrã de Informações adicionais
- Certifique-se de .NET 9.0 está selecionado.
- Verifique se Inscrever-se em .NET.NET Aspire de orquestração está marcado e, em seguida, selecione Criar.
builder.AddProject<Projects.AspireStorage_WorkerService>(
"aspirestorage-workerservice");
Visual Studio ferramentas adicionaram essa linha de código para registrar seu novo projeto com o objeto IDistributedApplicationBuilder, que permite recursos de orquestração. Para obter mais informações, consulte a visão geral da orquestração .NET.NET Aspire.
A estrutura da solução concluída deve ser semelhante à seguinte:
Adicionar as integrações .NET Aspire ao aplicativo Blazor
Adicione a integração .NET AspireAzure Blob Storage e os pacotes de integração do Queue Storage .NET AspireAzure ao seu projeto AspireStorage.Web:
dotnet add package Aspire.Azure.Storage.Blobs
dotnet add package Aspire.Azure.Storage.Queues
Seu projeto AspireStorage.Web agora está configurado para usar integrações .NET.NET Aspire. Aqui está o arquivo atualizado 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>
O próximo passo é adicionar as integrações ao aplicativo.
No arquivo
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();
Com as instruções using
adicionais, esses métodos realizam as seguintes tarefas:
- Registre um Azure.Storage.Blobs.BlobServiceClient e um Azure.Storage.Queues.QueueServiceClient com o contêiner DI para se conectar ao Azure Storage.
- Habilite automaticamente as verificações de integridade, o registro em log e a telemetria correspondentes para os respetivos serviços.
Quando o projeto AspireStorage.Web for iniciado, ele criará um contêiner de fileuploads
no Azurite Blob Storage e uma fila de tickets
no Azurite Queue Storage. Isso é condicional quando o aplicativo está sendo executado em um ambiente de desenvolvimento. Quando o aplicativo está sendo executado em um ambiente de produção, presume-se que o contêiner e a fila já tenham sido criados.
Adicione a integração .NET Aspire ao Worker Service
O serviço de trabalho encarrega-se de retirar mensagens da fila de armazenamento Azure para processamento. Adicione o pacote de integração .NET AspireAzure Queue Storage ao seu aplicativo AspireStorage.WorkerService:
dotnet add package Aspire.Azure.Storage.Queues
No arquivo
using AspireStorage.WorkerService;
var builder = Host.CreateApplicationBuilder(args);
builder.AddAzureQueueClient("QueueConnection");
builder.AddServiceDefaults();
builder.Services.AddHostedService<WorkerService>();
var host = builder.Build();
host.Run();
Este método lida com as seguintes tarefas:
- Registre um QueueServiceClient com o contêiner DI para se conectar a Azure Storage Queues.
- Habilite automaticamente as verificações de integridade, o registro em log e a telemetria correspondentes para os respetivos serviços.
Criar o formulário
A aplicação requer um formulário para que o utilizador possa submeter informações de pedido de suporte e anexar um ficheiro. A aplicação envia o ficheiro em anexo na propriedade Document
(IFormFile) para Azure Blob Storage utilizando o BlobServiceClientinjetado. O QueueServiceClient envia uma mensagem, composta por Title
e Description
, à fila de armazenamento Azure.
Use a seguinte marcação Razor para criar um formulário básico, substituindo o conteúdo do ficheiro Home.razor no diretório AspireStorage.Web/Components/Pages:
@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!;
}
}
Para obter mais informações sobre como criar formulários no Blazor, consulte ASP.NET CoreBlazor visão geral de formulários.
Atualizar o AppHost
O projeto AspireStorage.AppHost é o orquestrador do seu aplicativo. Ele é responsável por conectar e configurar os diferentes projetos e serviços do seu aplicativo. O orquestrador deve ser definido como o projeto de arranque.
Para adicionar o suporte de hospedagem do Azure Storage ao seu IDistributedApplicationBuilder, instale o 📦Aspire.Hospedagem.Azure.Armazenamento pacote NuGet.
- .NET CLI
- PackageReference
dotnet add package Aspire.Hosting.Azure.Storage
Substitua o conteúdo do arquivo
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();
O código anterior adiciona armazenamento Azure, blobs e filas e, quando no modo de desenvolvimento, usa o emulador. Cada projeto define referências para esses recursos dos quais depende.
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();
O código anterior adiciona Azure armazenamento, blobs e filas e define referências para esses recursos dentro de cada projeto que dependem deles.
Processar os itens na fila
Quando uma nova mensagem é colocada na fila de tickets
, o serviço de trabalho deve recuperar, processar e excluir a mensagem. Atualize a classe Worker.cs, substituindo o conteúdo pelo seguinte código:
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);
}
}
}
Antes que o serviço de trabalho possa processar mensagens, ele precisa ser capaz de se conectar à fila de armazenamento Azure. Com o Azurite, você precisa garantir que a fila esteja disponível antes que o serviço de trabalho comece a executar o processamento da fila de mensagens.
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);
}
}
}
O serviço de trabalho processa mensagens conectando-se à fila de armazenamento de Azure e retirando mensagens da fila.
O serviço de trabalho processa mensagens na fila e as exclui quando elas são processadas.
Configurar as cadeias de conexão
O projeto AspireStorage e o projeto AspireStorage.Worker devem ser configurados para se conectar à correta Conta de Armazenamento Azure que você criou anteriormente. Pode especificar os endpoints para os serviços de blob e fila na conta de armazenamento, utilizando o ficheiro appsettings.json em cada projeto.
No projeto AspireStorage, adicione a seguinte configuração ao arquivo
appsettings.Development.json
:"ConnectionStrings": { "BlobConnection": "https://<your-storage-account-name>.blob.core.windows.net/", "QueueConnection": "https://<your-storage-account-name>.queue.core.windows.net/" }
No projeto AspireStorage.Worker, adicione a seguinte configuração ao arquivo
appsettings.Development.json
:"ConnectionStrings": { "QueueConnection": "https://<your-storage-account-name>.queue.core.windows.net/" }
Executar e testar o aplicativo localmente
O aplicativo de exemplo agora está pronto para teste. Verifique se os dados do formulário enviado são enviados para o Azure Blob Storage e Azure Armazenamento de Filas concluindo as seguintes etapas:
Pressione o botão Executar na parte superior do Visual Studio para iniciar o painel do projeto .NET Aspire no navegador.
Na página de recursos, na linha aspirestorage.web, clique no link na coluna Pontos de Extremidade para abrir a interface de utilizador do seu aplicativo.
Insira dados de exemplo nos campos de formulário
Title
eDescription
e selecione um arquivo simples para carregar.Selecione o botão Enviar, e o formulário envia automaticamente o pedido de suporte para processamento e limpa o formulário.
Em uma guia separada do navegador, use o portal Azure para navegar até o navegador de armazenamento na sua conta de armazenamento Azure.
Selecione Contêineres e depois navegue até o contêiner Documentos para ver o arquivo carregado.
Você pode verificar se a mensagem na fila foi processada examinando o logs do Project do painel .NET.NET Aspiree selecionando o aspirestorage.workerservice na lista suspensa.
Resumo
O aplicativo de exemplo que você criou demonstra blobs persistentes de um ASP.NET CoreBlazor Web App e filas de processamento em um .NET Worker Service. A sua aplicação liga-se ao armazenamento Azure através de integrações .NET Aspire. A aplicação envia os pedidos de suporte para uma fila para processamento e carrega um anexo para armazenamento.
Como você opta por usar o Azurite, não há necessidade de limpar esses recursos quando terminar de testá-los, pois você os criou localmente no contexto de um emulador. O emulador permitiu que você testasse seu aplicativo localmente sem incorrer em custos, já que nenhum Azure recursos foram provisionados ou criados.
Limpar recursos
Execute o seguinte comando Azure CLI para excluir o grupo de recursos quando não precisar mais dos recursos Azure criados. A exclusão do grupo de recursos também exclui os recursos contidos nele.
az group delete --name <your-resource-group-name>
Para mais informações, veja Limpeza de recursos em Azure.