次の方法で共有


Entity Framework Core で .NET Aspire 移行を適用する

.NET .NET Aspire プロジェクトではコンテナー化されたアーキテクチャが使用されるため、データベースはエフェメラルであり、いつでも再作成できます。 Entity Framework Core (EF Core) では、移行 と呼ばれる機能を使用して、データベース スキーマを作成および更新します。 データベースはアプリの起動時に再作成されるため、アプリの起動時にデータベース スキーマを初期化するには、移行を適用する必要があります。 これを行うには、起動時に移行を実行する移行サービス プロジェクトをアプリに登録します。

このチュートリアルでは、アプリの起動時に .NET Aspire 移行を実行するように EF Core プロジェクトを構成する方法について説明します。

前提 条件

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

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

スターター アプリを入手する

このチュートリアルでは、EF Coreで .NET Aspire 移行を適用する方法を示すサンプル アプリを使用します。 を使用して、 からサンプル アプリ 複製するか、次のコマンドを使用します。

git clone https://github.com/MicrosoftDocs/aspire-docs-samples/

サンプル アプリは、SupportTicketApi フォルダーにあります。 Visual Studio または VS Code でソリューションを開き、少し時間を取ってサンプル アプリを確認し、実行されていることを確認してから続行します。 サンプル アプリは基本的なサポート チケット API であり、次のプロジェクトが含まれています。

  • SupportTicketApi.Api: API をホストする ASP.NET Core プロジェクト。
  • SupportTicketApi.Data: EF Core コンテキストとモデルが含まれています。
  • SupportTicketApi.AppHost: .NET.NET Aspire アプリのホストと構成が含まれています。
  • SupportTicketApi.ServiceDefaults: 既定のサービス構成が含まれています。

アプリを実行して、期待どおりに動作することを確認します。 ダッシュボードから、https Swagger エンドポイントを選択し、操作を展開して [試してみる] を選択して、API の GET /api/SupportTickets エンドポイント テストします。 の実行 選択して要求を送信し、応答を表示します。

[
  {
    "id": 1,
    "title": "Initial Ticket",
    "description": "Test ticket, please ignore."
  }
]

マイグレーションの作成

まず、適用する移行をいくつか作成します。

  1. ターミナルを開きます (+`Visual Studio)。

  2. SupportTicketApiSupportTicketApi.Api を現在のディレクトリとして設定します。

  3. dotnet ef コマンド ライン ツール を使用して、データベース スキーマの初期状態をキャプチャする新しい移行を作成します。

    dotnet ef migrations add InitialCreate --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj
    

    次のコマンドを実行します。

    • EF Core ディレクトリで移行コマンド ライン ツール 実行します。 API サービスが DB コンテキストを使用する場所であるため、dotnet ef はこの場所で実行されます。
    • InitialCreateという名前の移行を作成します。
    • SupportTicketApi.Data プロジェクトの Migrations フォルダーにマイグレーションを作成します。
  4. 新しいプロパティが含まれるようにモデルを変更します。 SupportTicketApi.DataModelsSupportTicket.cs を開き、SupportTicket クラスに新しいプロパティを追加します。

    public sealed class SupportTicket
    {
        public int Id { get; set; }
        [Required]
        public string Title { get; set; } = string.Empty;
        [Required]
        public string Description { get; set; } = string.Empty;
        public bool Completed { get; set; }
    }
    
  5. 別の新しい移行を作成して、モデルへの変更をキャプチャします。

    dotnet ef migrations add AddCompleted --project ..\SupportTicketApi.Data\SupportTicketApi.Data.csproj
    

これで、いくつかの移行を適用できるようになりました。 次に、アプリの起動時にこれらの移行を適用する移行サービスを作成します。

移行サービスを作成する

起動時に移行を実行するには、移行を適用するサービスを作成する必要があります。

  1. ソリューションに新しい Worker Service プロジェクトを追加します。 Visual Studioを使用している場合は、ソリューション エクスプローラーでソリューションを右クリックし、[Add>New Project] を選択します。 Worker Service を選択し、プロジェクトに SupportTicketApi.MigrationServiceという名前を付けます。 コマンド ラインを使用する場合は、ソリューション ディレクトリから次のコマンドを使用します。

    dotnet new worker -n SupportTicketApi.MigrationService
    dotnet sln add SupportTicketApi.MigrationService
    
  2. SupportTicketApi.Data またはコマンド ラインを使用して、SupportTicketApi.ServiceDefaults および SupportTicketApi.MigrationService プロジェクト参照を Visual Studio プロジェクトに追加します。

    dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.Data
    dotnet add SupportTicketApi.MigrationService reference SupportTicketApi.ServiceDefaults
    
  3. 📦 Aspireを追加します。Microsoft.EntityFrameworkCore.SqlServerSupportTicketApi.MigrationService またはコマンド ラインを使用して、Visual Studio プロジェクトへの NuGet パッケージ参照を作成します。

    dotnet add package Aspire.Microsoft.EntityFrameworkCore.SqlServer
    
  4. 強調表示された行を、Program.cs プロジェクトの SupportTicketApi.MigrationService ファイルに追加します。

    using SupportTicketApi.Data.Contexts;
    using SupportTicketApi.MigrationService;
    
    var builder = Host.CreateApplicationBuilder(args);
    
    builder.AddServiceDefaults();
    builder.Services.AddHostedService<Worker>();
    
    builder.Services.AddOpenTelemetry()
        .WithTracing(tracing => tracing.AddSource(Worker.ActivitySourceName));
    
    builder.AddSqlServerDbContext<TicketContext>("sqldata");
    
    var host = builder.Build();
    host.Run();
    

    前のコードでは、次のようになります。

    • AddServiceDefaults 拡張メソッド サービスの既定の機能が追加されます。
    • AddOpenTelemetry の拡張メソッド は OpenTelemetry 機能を構成します。
    • AddSqlServerDbContext 拡張メソッドは、TicketContext サービスをサービス コレクションに追加します。 このサービスは、移行の実行とデータベースのシード処理に使用されます。
  5. Worker.cs プロジェクトの SupportTicketApi.MigrationService ファイルの内容を次のコードに置き換えます。

    using System.Diagnostics;
    
    using Microsoft.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore.Infrastructure;
    using Microsoft.EntityFrameworkCore.Storage;
    
    using OpenTelemetry.Trace;
    
    using SupportTicketApi.Data.Contexts;
    using SupportTicketApi.Data.Models;
    
    namespace SupportTicketApi.MigrationService;
    
    public class Worker(
        IServiceProvider serviceProvider,
        IHostApplicationLifetime hostApplicationLifetime) : BackgroundService
    {
        public const string ActivitySourceName = "Migrations";
        private static readonly ActivitySource s_activitySource = new(ActivitySourceName);
    
        protected override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            using var activity = s_activitySource.StartActivity("Migrating database", ActivityKind.Client);
    
            try
            {
                using var scope = serviceProvider.CreateScope();
                var dbContext = scope.ServiceProvider.GetRequiredService<TicketContext>();
    
                await EnsureDatabaseAsync(dbContext, cancellationToken);
                await RunMigrationAsync(dbContext, cancellationToken);
                await SeedDataAsync(dbContext, cancellationToken);
            }
            catch (Exception ex)
            {
                activity?.RecordException(ex);
                throw;
            }
    
            hostApplicationLifetime.StopApplication();
        }
    
        private static async Task EnsureDatabaseAsync(TicketContext dbContext, CancellationToken cancellationToken)
        {
            var dbCreator = dbContext.GetService<IRelationalDatabaseCreator>();
    
            var strategy = dbContext.Database.CreateExecutionStrategy();
            await strategy.ExecuteAsync(async () =>
            {
                // Create the database if it does not exist.
                // Do this first so there is then a database to start a transaction against.
                if (!await dbCreator.ExistsAsync(cancellationToken))
                {
                    await dbCreator.CreateAsync(cancellationToken);
                }
            });
        }
    
        private static async Task RunMigrationAsync(TicketContext dbContext, CancellationToken cancellationToken)
        {
            var strategy = dbContext.Database.CreateExecutionStrategy();
            await strategy.ExecuteAsync(async () =>
            {
                // Run migration in a transaction to avoid partial migration if it fails.
                await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
                await dbContext.Database.MigrateAsync(cancellationToken);
                await transaction.CommitAsync(cancellationToken);
            });
        }
    
        private static async Task SeedDataAsync(TicketContext dbContext, CancellationToken cancellationToken)
        {
            SupportTicket firstTicket = new()
            {
                Title = "Test Ticket",
                Description = "Default ticket, please ignore!",
                Completed = true
            };
    
            var strategy = dbContext.Database.CreateExecutionStrategy();
            await strategy.ExecuteAsync(async () =>
            {
                // Seed the database
                await using var transaction = await dbContext.Database.BeginTransactionAsync(cancellationToken);
                await dbContext.Tickets.AddAsync(firstTicket, cancellationToken);
                await dbContext.SaveChangesAsync(cancellationToken);
                await transaction.CommitAsync(cancellationToken);
            });
        }
    }
    

    前のコードでは、次のようになります。

    • ExecuteAsync メソッドは、ワーカーの起動時に呼び出されます。 次に、次の手順を実行します。
      1. サービス プロバイダーから TicketContext サービスへの参照を取得します。
      2. データベースが存在しない場合は、EnsureDatabaseAsync を呼び出してデータベースを作成します。
      3. 未処理の移行を適用するために RunMigrationAsync を呼び出します。
      4. SeedDataAsync を呼び出して、初期データをデータベースにシード処理します。
      5. StopApplicationでワーカーを停止します。
    • EnsureDatabaseAsyncRunMigrationAsync、および SeedDataAsync メソッドはすべて、実行戦略を使用してそれぞれのデータベース操作をカプセル化し、データベースとの対話時に発生する可能性のある一時的なエラーを処理します。 実行戦略の詳細については、「接続の回復性」に関するページを参照してください。

オーケストレーターに移行サービスを追加する

移行サービスは作成されますが、アプリの起動時に実行されるように、.NET.NET Aspire アプリ ホストに追加する必要があります。

  1. SupportTicketApi.AppHost プロジェクトで、Program.cs ファイルを開きます。

  2. ConfigureServices メソッドに、次の強調表示されたコードを追加します。

    var builder = DistributedApplication.CreateBuilder(args);
    
    var sql = builder.AddSqlServer("sql")
                     .AddDatabase("sqldata");
    
    builder.AddProject<Projects.SupportTicketApi_Api>("api")
        .WithReference(sql);
    
    builder.AddProject<Projects.SupportTicketApi_MigrationService>("migrations")
        .WithReference(sql);
    
    builder.Build().Run();
    

    これにより、SupportTicketApi.MigrationService.NET アプリ ホストにサービスとして .NET Aspire プロジェクトが参加します。

    大事な

    Visual Studioを使用していて、Enlist in Aspire orchestration プロジェクトの作成時に Worker Service オプションを選択した場合は、サービス名 supportticketapi-migrationserviceで同様のコードが自動的に追加されます。 そのコードを前のコードに置き換えます。

既存のシード 処理コードを削除する

移行サービスによってデータベースがシードされるため、既存のデータ シード 処理コードを API プロジェクトから削除する必要があります。

  1. SupportTicketApi.Api プロジェクトで、Program.cs ファイルを開きます。

  2. 強調表示されている行を削除します。

    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    
        using (var scope = app.Services.CreateScope())
        {
            var context = scope.ServiceProvider.GetRequiredService<TicketContext>();
            context.Database.EnsureCreated();
    
            if(!context.Tickets.Any())
            {
                context.Tickets.Add(new SupportTicket { Title = "Initial Ticket", Description = "Test ticket, please ignore." });
                context.SaveChanges();
            }
        }
    }
    

移行サービスをテストする

移行サービスが構成されたので、アプリを実行して移行をテストします。

  1. アプリを実行し、SupportTicketApi ダッシュボードを確認します。

  2. しばらく待つと、migrations サービスの状態が 完了を表示します。

    移行サービスが完了状態の .NET.NET Aspire ダッシュボードのスクリーンショット。

  3. 移行サービスの View リンクを選択して、実行された SQL コマンドを示すログを調査します。

コードを取得する

完成したサンプル アプリは、GitHubにあります。

その他のサンプル コード

Aspire Shop サンプル アプリでは、このアプローチを使用して移行を適用します。 移行サービスの実装については、AspireShop.CatalogDbManager プロジェクトを参照してください。