次の方法で共有


チュートリアル: ASP.NET Core アプリを .NET Aspire ストレージ統合に接続する

多くの場合、クラウドネイティブ アプリには、BLOB ストレージ、キュー、半構造化 NoSQL データベースなどの機能を提供するスケーラブルなストレージ ソリューションが必要です。 .NET Aspire 統合により、Azure Blob Storageなど、さまざまなストレージ サービスへの接続が簡略化されます。 このチュートリアルでは、ASP.NET Core アプリを作成し、.NET Aspire 統合を使用して Azure Blob Storage および Azure Queue Storage に接続し、サポート チケットを送信します。 アプリは、処理のためにチケットをキューに送信し、添付ファイルをストレージにアップロードします。 次の方法について学習します。

  • .NET 統合を使用するように設定された基本的な .NET Aspire アプリを作成する
  • .NET .NET Aspire 統合を追加して複数のストレージ サービスに接続する
  • .NET .NET Aspire コンポーネント機能を構成して使用してデータを送受信する

前提 条件

.NET .NET Aspireを使用するには、次のものがローカルにインストールされている必要があります。

詳細については、 セットアップとツールの、および SDK を参照してください。

完成したサンプル アプリを調べる

このチュートリアルのサンプル アプリの完成版は、GitHubで入手できます。 プロジェクトは、のテンプレートとしても構成されています。つまり、 コマンドを使用して、ツール インストールされている場合は、 リソースのプロビジョニングを自動化できます。

git clone https://github.com/Azure-Samples/dotnet-aspire-connect-storage.git

Azure Storage リソースを設定する

この記事では、BLOB コンテナーとストレージ キューを持つ Azure ストレージ アカウントへのデータ共同作成者アクセス権が必要です。 次のリソースと構成を使用できることを確認します。

この記事では、エミュレーターを使用して、ローカル開発環境で BLOB コンテナーとストレージ キュー リソースを作成する必要があります。 これを行うには、Azurite を使用します。 Azurite は、Azure コンテナーで実行される無料のオープン ソースのクロスプラットフォーム server Storage API 互換 Docker (エミュレーター) です。

エミュレーターを使用するには、Azuriteを インストールする必要があります。

  1. Azure ストレージ アカウント - ストレージ アカウントを作成します。
  2. fileuploads という名前の Blob Storage コンテナー - BLOB ストレージ コンテナーを作成します。
  3. チケットという名前のストレージ キュー - ストレージ キューを作成します。

Azure CLI または CloudShell で次のコマンドを実行して、必要な 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

また、Visual Studio にログインしているユーザー アカウントに次のロールを割り当てる必要があります。

Azure Developer CLI を使用すると、テンプレート システムを使用して Azure リソースをプロビジョニングおよびデプロイできます。 このチュートリアルでは、必要な リソースをプロビジョニングし、完成したサンプル アプリケーション コードを含む Azure を提供します。 次のコマンドを実行して、テンプレートを初期化して実行します。

  1. azd auth login を実行して、Azureにサインインします。

    azd auth login
    
  2. azd init を実行して、サンプル テンプレートを複製して初期化します。

    azd init --template dotnet-aspire-connect-storage
    
  3. azd up を実行して、Azure リソースをプロビジョニングします。

    azd up
    
  4. メッセージが表示されたら、プロビジョニングされたリソースのサブスクリプションと Azure リージョンを選択します。 テンプレートが実行され、次のタスクが完了します。

    • BLOB サービスとキュー サービスが有効になっている Azure ストレージ アカウントを作成します
    • fileUploads という名前の BLOB ストレージ コンテナーを作成します。
    • tickets という名前のキューを作成します
    • テンプレートを実行したユーザー アカウントに次のロールを割り当てます。
      • ストレージ BLOB データ共同寄稿者
      • ストレージ キュー データ共同作成者

操作が正常に完了したら、次のステップに進むための2つの選択肢があります。

  • オプション 1: テンプレート .NET ディレクトリで src サンプル アプリを実行し、完成したアプリを試します。
  • オプション 2: 前のセクションを使用して、サンプル アプリを段階的にビルドし、Azureによってプロビジョニングされた azd リソースに接続します。

サンプル ソリューションを作成する

.NET Aspire または Visual Studio CLI を使用して、.NET プロジェクトを作成します。

  1. Visual Studioの上部にある [ファイル]>[新しい>プロジェクト] に移動します。
  2. ダイアログ ウィンドウで、 を検索し、スターター アプリケーション 選択します。 次にを選択します。
  3. [新しいプロジェクトの構成] 画面で、次の手順を実行します。
    • ソリューション名 を入力し、[次へ]選択します。
  4. 追加情報 画面で、次の手順を実行します。
    • のチェックボックスをオフにして、キャッシュにRedisを使用する(このチュートリアルでは必要ありません)。
    • を選択し、を作成します。

Visual Studio は、ASP.NET Coreを使用するように構成された新しい .NET Aspire ソリューションを作成します。

このソリューションは、次のプロジェクトで構成されます。

  • AspireStorage.ApiService - 既定の .NET.NET Aspire サービス構成を持つ API プロジェクト。
  • AspireStorage.AppHost - アプリのさまざまなプロジェクトとサービスを接続して構成するように設計されたオーケストレーター プロジェクトです。 オーケストレーターはスタートアップ プロジェクトとして設定する必要があります。
  • AspireStorage.ServiceDefaults - ソリューション内のプロジェクト間で再利用できるコードを保持するための共有クラス ライブラリです。
  • AspireStorage.Web - アプリのフロントエンドとして機能する BlazorServer プロジェクトです。

Worker Service プロジェクトを追加する

次に、Worker Service プロジェクトをソリューションに追加して、メッセージが Azure Storage キューに追加されると、メッセージを取得して処理します。

  1. ソリューション エクスプローラーで、ソリューション ノード 最上位レベル 右クリックし、[新しいプロジェクト追加] 選択します。
  2. Worker Service テンプレートを検索して選択し、「次へ」をクリックします。
  3. プロジェクト名に「AspireStorage.WorkerService」と入力し、次へを選択します。
  4. 追加情報 画面で、次の手順を実行します。
    • .NET 9.0 が選択されていることを確認します。
    • オーケストレーション に参加 オンになっていることを確認し、[の作成] 選択します。

Visual Studio ソリューションにプロジェクトを追加し、新しいコード行で Program.cs プロジェクトの ファイルを更新します。

builder.AddProject<Projects.AspireStorage_WorkerService>(
    "aspirestorage-workerservice");

Visual Studio ツールは、オーケストレーション機能を有効にする IDistributedApplicationBuilder オブジェクトに新しいプロジェクトを登録するために、このコード行を追加しました。 詳細については、オーケストレーションの概要 参照してください。

完成したソリューション構造は次のようになります。

.NET.NET Aspire ストレージ サンプル ソリューションの構造を示すスクリーンショット。

.NET Aspire 統合を Blazor アプリに追加する

.NET Aspire プロジェクトに、.NET Aspire と Azure Queue Storage 統合 パッケージを追加します。

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>

次の手順では、統合をアプリに追加します。

Program.cs プロジェクトの ファイルで、AddAzureBlobClient の作成後、AddAzureQueueClientの呼び出しの前に、builder および AddServiceDefaults 拡張メソッドの呼び出しを追加します。 詳細については、「.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 ステートメントを使用して、次のタスクを実行します。

AspireStorage.Web プロジェクトが開始されると、Azurite Blob Storage に fileuploads コンテナーが作成され、Azurite Queue Storage に tickets キューが作成されます。 これは、アプリが開発環境で実行されている場合に条件付きです。 アプリが運用環境で実行されている場合、コンテナーとキューは既に作成されていると見なされます。

.NET Aspire 統合を Worker Service に追加する

ワーカー サービスは、処理のために Azure Storage キューからメッセージをプルする処理を行います。 .NET Aspire Azure Queue Storage 統合 統合パッケージを AspireStorage.WorkerService アプリに追加します。

dotnet add package Aspire.Azure.Storage.Queues

Program.cs プロジェクトの ファイルで、AddAzureQueueClient の作成後、builderの呼び出しの前に、AddServiceDefaults 拡張メソッドの呼び出しを追加します。

using AspireStorage.WorkerService;

var builder = Host.CreateApplicationBuilder(args);

builder.AddAzureQueueClient("QueueConnection");

builder.AddServiceDefaults();
builder.Services.AddHostedService<WorkerService>();

var host = builder.Build();
host.Run();

このメソッドは、次のタスクを処理します。

  • QueueServiceClientに接続するための DI コンテナーに Azure Storage Queues を登録します。
  • 対応する正常性チェック、ログ記録、テレメトリをそれぞれのサービスに対して自動的に有効にします。

フォームを作成する

アプリでは、ユーザーがサポート チケット情報を送信して添付ファイルをアップロードできるようにするためのフォームが必要です。 アプリは、挿入された Documentを使用して、IFormFile (Azure Blob Storage) プロパティに添付ファイルを BlobServiceClient にアップロードします。 QueueServiceClient は、TitleDescription で構成されるメッセージを 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!;
    }
}

でフォームを作成する方法の詳細については、「フォームの概要 参照してください。

AppHost を更新する

AspireStorage.AppHost プロジェクトは、アプリのオーケストレーターです。 アプリのさまざまなプロジェクトとサービスを接続して構成する必要があります。 オーケストレーターはスタートアップ プロジェクトとして設定する必要があります。

Azure Storage ホスティングのサポートを IDistributedApplicationBuilderに追加するには、📦Aspire.Hosting.Azure.Storage NuGet パッケージをインストールします。

dotnet add package Aspire.Hosting.Azure.Storage

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();

上記のコードは、ストレージ、BLOB、キュー Azure 追加し、開発モードの場合はエミュレーターを使用します。 各プロジェクトは、依存するこれらのリソースの参照を定義します。

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();

上記のコードは、ストレージ、BLOB、キュー Azure 追加し、それらに依存する各プロジェクト内のこれらのリソースの参照を定義します。

キュー内の項目を処理する

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 Storage キューに接続できる必要があります。 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 Storage キューに接続し、キューからメッセージをプルすることで、メッセージを処理します。

ワーカー サービスはキュー内のメッセージを処理し、処理されるとそれらを削除します。

接続文字列を構成する

AspireStorage および AspireStorage.Worker プロジェクトは、前に作成した適切な Azure ストレージ アカウントに接続するように構成する必要があります。 各プロジェクトの appsettings.json ファイルを使用して、ストレージ アカウント内の BLOB サービスとキュー サービスのエンドポイントを指定できます。

  1. 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/"
      }
    
  2. AspireStorage.Worker プロジェクトで、appsettings.Development.json ファイルに次の構成を追加します。

      "ConnectionStrings": {
        "QueueConnection": "https://<your-storage-account-name>.queue.core.windows.net/"
      }
    

アプリをローカルで実行してテストする

これで、サンプル アプリをテストする準備ができました。 次の手順を実行して、送信されたフォーム データが Azure Blob Storage および Azure Queue Storage に送信されることを確認します。

  1. Visual Studio の上部にある実行ボタンを押して、ブラウザーで .NET Aspire プロジェクト ダッシュボードを起動します。

  2. [リソース] ページの 行で、[エンドポイント] 列のリンクをクリックして、アプリの UI を開きます。

    .NET.NET Aspire サポート アプリケーションのホーム ページを示すスクリーンショット。

  3. Title フィールドと Description フォーム フィールドにサンプル データを入力し、アップロードする単純なファイルを選択します。

  4. [送信] ボタンを選択すると、フォームは処理のためにサポート チケットを送信し、フォームをクリアします。

  5. 別のブラウザー タブで、Azure ポータルを使用して、 ストレージ アカウントの Azure に移動します。

  6. コンテナー を選択し、Documents コンテナーに移動して、アップロードされたファイルを表示します。

  7. キューのメッセージが処理されたことを確認するには、.NET Aspireの Project ログ を確認し、ドロップダウンから aspirestorage.workerservice を選択します。

    Worker アプリのコンソール出力を示すスクリーンショット。

概要

作成したサンプル アプリは、ASP.NET CoreBlazor Web アプリからの BLOB の永続化と、.NET Worker Service内のキューの処理を示しています。 アプリは、Azure 統合を使用して .NET Aspire Storage に接続します。 アプリは、処理のためにサポート チケットをキューに送信し、添付ファイルをストレージにアップロードします。

Azurite を使用することを選択しているため、エミュレーターのコンテキストでローカルに作成したので、テストが完了したときにこれらのリソースをクリーンアップする必要はありません。 エミュレーターを使用すると、Azure リソースがプロビジョニングまたは作成されていないため、コストをかけずにアプリをローカルでテストできます。

リソースのクリーンアップ

作成した Azure リソースが不要になったら、次の Azure CLI コマンドを実行してリソース グループを削除します。 リソース グループを削除すると、その中に含まれるリソースも削除されます。

az group delete --name <your-resource-group-name>

詳細については、リソースをクリーンアップする方法を参照してください。