다음을 통해 공유


Entity Framework Core 마이그레이션을 .NET Aspire에 적용

.NET .NET Aspire 프로젝트는 컨테이너화된 아키텍처를 사용하므로 데이터베이스는 임시이며 언제든지 다시 만들 수 있습니다. Entity Framework Core(EF Core)는 마이그레이션 호출된 기능을 사용하여 데이터베이스 스키마를 만들고 업데이트합니다. 앱이 시작될 때 데이터베이스가 다시 만들어지므로 앱이 시작될 때마다 마이그레이션을 적용하여 데이터베이스 스키마를 초기화해야 합니다. 이 작업은 시작하는 동안 마이그레이션을 실행하는 마이그레이션 서비스 프로젝트를 앱에 등록하여 수행됩니다.

이 자습서에서는 앱을 시작하는 동안 .NET Aspire 마이그레이션을 실행하도록 EF Core 프로젝트를 구성하는 방법에 대해 알아봅니다.

필수 구성 요소

.NET .NET Aspire작업하려면 다음을 로컬에 설치해야 합니다.

자세한 내용은 .NET.NET Aspire 설정 및 도구.NET.NET Aspire SDK참조하세요.

시작 앱 가져오기

이 자습서는 EF Core에서 .NET Aspire 마이그레이션을 적용하는 방법을 보여주는 예제 앱을 사용합니다. 샘플 앱을 Visual Studio으로 에서 GitHub 복제하거나 다음 명령을 사용하세요.

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: 기본 서비스 구성을 포함합니다.

앱을 실행하여 예상대로 작동하는지 확인합니다. .NET .NET Aspire 대시보드에서 https Swagger 엔드포인트를 선택하고 작업을 확장한 후 Try it out을 선택하여 API의 GET /api/SupportTickets 엔드포인트를 테스트합니다. 실행을 선택하여 요청을 보내고 응답을 확인합니다.

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

마이그레이션 만들기

적용할 마이그레이션을 만들어 시작합니다.

  1. Ctrl을 사용하여 터미널()을 엽니다.

  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. Visual Studio.

    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사용하여 작업자를 중지합니다.
    • EnsureDatabaseAsync, RunMigrationAsyncSeedDataAsync 메서드는 모두 실행 전략을 사용하여 해당 데이터베이스 작업을 캡슐화하여 데이터베이스와 상호 작용할 때 발생할 수 있는 일시적인 오류를 처리합니다. 실행 전략에 대한 자세한 내용은 연결 복원력참조하세요.

오케스트레이터에 마이그레이션 서비스 추가

마이그레이션 서비스가 만들어지지만 앱이 시작될 때 실행되도록 .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 프로젝트를 참조하세요.