Tutorial: Conectar um aplicativo ASP.NET Core a integrações de armazenamento .NET Aspire
Os aplicativos nativos de nuvem geralmente exigem soluções de armazenamento escalonáveis que fornecem recursos como armazenamento de blobs, 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, você criará um aplicativo ASP.NET Core que utiliza integrações .NET Aspire para se conectar aos Armazenamentos de Filas Azure Blob Storage e Azure para enviar tíquetes 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 básico de .NET configurado para utilizar as integrações de .NET Aspire
- Adicionar integrações .NET.NET Aspire para se conectar a vários serviços de armazenamento
- Configurar e usar recursos do componente .NET.NET Aspire 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 runtime de contêiner compatível com OCI, como:
- Docker computador de mesa ou Podman. Para obter mais informações, consulte runtime do contêiner.
- Um IDE (Ambiente de Desenvolvedor Integrado) ou um 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 plug-in (opcional)
Para obter mais informações, consulte .NET.NET Aspirede instalação e ferramentas e .NET.NET Aspiredo SDK.
Explorar o aplicativo de exemplo concluído
Uma versão concluída do aplicativo de exemplo deste tutorial está disponível no 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 do Azure, caso tenha 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á ter acesso de colaborador de dados a uma conta de Armazenamento Azure contendo um contêiner de blobs e uma fila de armazenamento. Verifique se você tem 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 o Azurite. O Azurite é um Azure (emulador) compatível com a API de Armazenamento server de software livre e de software livre que é executado em um contêiner de Docker.
Para usar o emulador, você precisa instalar o Azurite.
- Uma conta de Armazenamento Azure – Criar uma conta de armazenamento.
- Um contêiner de Armazenamento de Blobs chamado fileuploads - . Criar um contêiner de Armazenamento de Blobs.
- Uma fila de armazenamento chamada tíquetes - . Criar uma fila de armazenamento.
Execute os seguintes comandos na CLI Azure ou no CloudShell para configurar os recursos de armazenamento de Azure necessários:
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 na qual você está conectado com Visual Studio:
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 entrar 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 de Azure:azd up
Quando solicitado, selecione a assinatura e a região Azure dos recursos provisionados. O modelo executa 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 blobs chamado
fileUploads
- Cria uma fila chamada
tickets
- Atribui as seguintes funções à conta de usuário que executou o modelo.
- Colaborador de Dados do Blob de Armazenamento
- Colaborador 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
src
modelo para experimentar o aplicativo concluído. - Opção 2: criar o aplicativo de exemplo passo a passo usando as seções à frente e conectá-lo aos recursos de Azure provisionados pelo
azd
.
Criar a solução de exemplo
Crie um projeto .NET Aspire usando Visual Studio ou a CLI do .NET.
- Na parte superior do Visual Studio, navegue até >Nova>Projeto.
- Na janela de diálogo, pesquise Aspire e selecione .NET.NET Aspire Aplicação Inicial. Escolha Próximo.
- Na tela Configurar seu novo projeto:
- Insira um de Nome da Solução
do AspireStorage e selecione Próximo .
- Insira um de Nome da Solução
- Na tela Informações adicionais:
- Desmarque Usar Redis para armazenar em cache (não necessário para este tutorial).
- Selecione Criar.
Visual Studio cria uma nova solução ASP.NET Core estruturada para usar .NET Aspire.
A solução consiste nos seguintes projetos:
- AspireStorage.ApiService – um projeto de API com configurações de serviço de .NET.NET Aspire padrão.
- AspireStorage.AppHost – um projeto de orquestrador projetado para conectar e configurar os diferentes projetos e serviços do seu aplicativo. O orquestrador deve ser definido como o projeto de inicialização.
- AspireStorage.ServiceDefaults – uma biblioteca de classes compartilhada para manter o código que pode ser reutilizado entre 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 de Worker Service à solução para recuperar e processar mensagens conforme elas são adicionadas à fila de Armazenamento Azure.
- No gerenciador de soluções, clique com o botão direito do mouse no nó principal da solução AspireStorage e selecione Adicionar>Novo projeto.
- Pesquise e selecione o modelo Worker Service e escolha Próximo.
- Para o nome do projeto , insira AspireStorage.WorkerService e selecione Avançar.
- Na tela Informações adicionais:
- Verifique se .NET 9.0 está selecionado.
- Verifique se Inscrever-se em .NET.NET Aspire de orquestração está marcada e selecione Criar.
Visual Studio adiciona o projeto à sua solução e atualiza o arquivo Program.cs do projeto AspireStorage.AppHost com uma nova linha de código:
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 de solução concluída deve ser semelhante à seguinte:
Adicionar as integrações de .NET Aspire ao aplicativo Blazor
Adicione os pacotes de integração .NET AspireAzure Blob Storage e .NET AspireAzure do Armazenamento de Filas 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>
A próxima etapa é adicionar as integrações ao aplicativo.
No arquivo Program.cs do projeto AspireStorage.Web, adicione chamadas aos métodos de extensão AddAzureBlobClient e AddAzureQueueClient após a criação do builder
, mas antes da chamada para AddServiceDefaults
. Para obter mais informações, consulte .NET.NET Aspire configurações padrão de serviço. Forneça o nome da cadeia de conexão como um parâmetro.
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 de using
adicionais, esses métodos realizam as seguintes tarefas:
- Registre um Azure.Storage.Blobs.BlobServiceClient e um Azure.Storage.Queues.QueueServiceClient no contêiner de DI para se conectar ao Armazenamento Azure.
- Habilite automaticamente as verificações de integridade, o registro em log e a telemetria correspondentes para os respectivos serviços.
Quando o projeto AspireStorage.Web for iniciado, ele criará um contêiner de fileuploads
no Armazenamento de Blobs do Azurite e uma fila de tickets
no Armazenamento de Filas do Azurite. Isso é condicional quando o aplicativo está em execução em um ambiente de desenvolvimento. Quando o aplicativo está em execução em um ambiente de produção, supõe-se que o contêiner e a fila já tenham sido criados.
Adicionar a integração .NET Aspire ao Worker Service
O serviço de trabalho lida com a retirada de mensagens da fila de armazenamento do Azure para processamento. Adicione o pacote de integração do .NET AspireAzure Queue Storage ao seu aplicativo AspireStorage.WorkerService.
dotnet add package Aspire.Azure.Storage.Queues
No arquivo Program.cs do projeto AspireStorage.WorkerService, adicione uma chamada ao método de extensão AddAzureQueueClient após a criação do builder
, mas antes da chamada para AddServiceDefaults
:
using AspireStorage.WorkerService;
var builder = Host.CreateApplicationBuilder(args);
builder.AddAzureQueueClient("QueueConnection");
builder.AddServiceDefaults();
builder.Services.AddHostedService<WorkerService>();
var host = builder.Build();
host.Run();
Esse método lida com as seguintes tarefas:
- Registre um QueueServiceClient com o contêiner de DI para se conectar ao Azure Storage Queues.
- Habilite automaticamente as verificações de integridade, o registro em log e a telemetria correspondentes para os respectivos serviços.
Criar o formulário
O aplicativo precisa de um formulário para que o usuário possa enviar as informações do tíquete de suporte e fazer o upload de um anexo. O aplicativo carrega o arquivo anexado na propriedade Document
(IFormFile) para Azure Blob Storage usando o BlobServiceClientinjetado. O QueueServiceClient envia uma mensagem composta por Title
e Description
para a Fila de Armazenamento Azure.
Utilize a seguinte marcação Razor para criar um formulário básico, substituindo o conteúdo do arquivo 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 em Blazor, consulte ASP.NET CoreBlazor visão geral dos 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 inicialização.
Para adicionar Azure suporte de hospedagem de Armazenamento 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 acima adiciona armazenamento Azure, blobs e filas; e, quando está no modo de desenvolvimento, utiliza o emulador. Cada projeto define referências para esses recursos dos quais eles dependem.
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 armazenamento Azure, blobs e filas, e define referências a esses recursos em cada projeto que depende deles.
Processar os itens na fila
Quando uma nova mensagem é colocada na fila 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 Azure e tirando mensagens da fila.
O serviço de trabalho processa as mensagens na fila e as exclui quando elas são processadas.
Configurar as cadeias de conexão
Os projetos AspireStorage e AspireStorage.Worker devem ser configurados para se conectar à conta de armazenamento correta Azure que você criou anteriormente. Você pode especificar os pontos de extremidade para os serviços de blobs e filas na conta de armazenamento usando o arquivo 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 de formulário enviados são enviados para 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 seu painel de 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 do usuário do aplicativo.
Insira dados de exemplo nos campos
Title
eDescription
formulário e selecione um arquivo simples para carregar.Selecione o botão Enviar e o formulário envia o tíquete de suporte para processamento e limpa o formulário.
Em uma guia separada do navegador, use o portal
para navegar até a do navegador de Armazenamento em sua conta de armazenamento do . Selecione
contêineres e navegue até o contêiner documentospara ver o arquivo carregado. Você pode verificar se a mensagem na fila foi processada consultando os logs do projeto no painel .NET.NET Aspiree selecionando o aspirestorage.workerservice no menu suspenso.
Resumo
O aplicativo de exemplo que você criou demonstra a persistência de blobs de um Aplicativo Web ASP.NET CoreBlazor e o processamento de filas em um .NET Worker Service. Seu aplicativo se conecta ao Armazenamento Azure usando as integrações .NET Aspire. O aplicativo envia os tíquetes de suporte para uma fila para processamento e faz upload de um anexo para o armazenamento.
Como você opta por usar o Azurite, não é necessário limpar esses recursos quando terminar de testá-los, pois os criou localmente no contexto de um emulador. O emulador permitiu que você testasse seu aplicativo localmente sem incorrer em custos, pois nenhum recurso Azure foi provisionado ou criado.
Limpar recursos
Execute o seguinte comando Azure CLI para excluir o grupo de recursos quando você não precisar mais dos recursos de Azure que você criou. Excluir o grupo de recursos também exclui os recursos contidos nele.
az group delete --name <your-resource-group-name>
Para obter mais informações, consulte Limpeza de recursos no Azure.