4부. ASP.NET Core에서 EF Core 마이그레이션을 사용한 Razor Pages

작성자: Tom Dykstra, Jon P SmithRick Anderson

Contoso University 웹앱은 EF Core 및 Visual Studio를 사용하여 Razor Pages 웹앱을 만드는 방법을 보여줍니다. 자습서 시리즈에 대한 정보는 첫 번째 자습서를 참조합니다.

해결할 수 없는 문제가 발생한 경우 완성된 앱을 다운로드하고 자습서를 따라 만든 코드와 해당 코드를 비교합니다.

이 자습서에서는 데이터 모델 변경 관리를 위한 EF Core 마이그레이션 기능을 도입합니다.

새 앱이 개발되면 데이터 모델이 자주 변경됩니다. 모델이 변경될 때마다 모델이 데이터베이스와 동기화 해제됩니다. 이 자습서 시리즈는 존재하지 않는 경우 데이터베이스를 만들도록 Entity Framework를 구성하여 시작되었습니다. 데이터 모델이 변경될 때마다 데이터베이스를 삭제해야 합니다. 다음에 앱이 실행되면 EnsureCreated가 호출되어 새 데이터 모델과 일치하도록 데이터베이스를 다시 만듭니다. 그런 다음, DbInitializer 클래스가 실행되어 새 데이터베이스를 시드합니다.

DB를 데이터 모델과 동기화된 상태로 유지하는 접근 방식은 앱을 제작 환경에 배포할 때까지 잘 작동하게 합니다. 앱이 프로덕션 환경에서 실행 중인 경우 일반적으로 유지 관리해야 하는 데이터를 저장합니다. 앱은 변경(예: 새 열 추가)될 때마다 테스트 DB로 시작될 수 없습니다. EF Core 마이그레이션 기능은 EF Core에서 새 데이터베이스를 만드는 대신 DB 스키마를 업데이트하도록 하여 이 문제를 해결합니다.

데이터 모델이 변경될 때 데이터베이스를 삭제하고 다시 작성하는 대신 마이그레이션은 스키마를 업데이트하고 기존 데이터를 유지합니다.

참고 항목

SQLite 제한 사항

이 자습서에서는 가능한 경우 Entity Framework Core ‘마이그레이션’ 기능을 사용합니다. 마이그레이션은 데이터 모델의 변경 내용과 일치하도록 데이터베이스 스키마를 수정합니다. 그러나 마이그레이션은 데이터베이스 엔진에서 지원하는 종류의 변경만 수행하며 SQLite의 스키마 변경 기능은 제한됩니다. 예를 들어 열 추가는 지원되지만 열 제거는 지원되지 않습니다. 열을 제거하기 위해 마이그레이션을 만들면 ef migrations add 명령은 성공하지만 ef database update 명령은 실패합니다.

SQLite 제한 사항에 대한 해결 방법은 테이블의 내용이 변경되면 테이블을 다시 빌드하기 위해 수동으로 마이그레이션 코드를 작성하는 것입니다. 코드는 마이그레이션을 위한 UpDown 메서드에 포함되고 다음을 포함합니다.

  • 새 테이블 만들기.
  • 이전 테이블에서 새 테이블로 데이터 복사.
  • 이전 테이블 삭제.
  • 새 테이블 이름 바꾸기.

이 형식의 데이터베이스 관련 코드를 작성하는 방법은 이 자습서에서 다루지 않습니다. 대신, 이 자습서에서는 마이그레이션을 적용하려는 시도가 실패할 때마다 데이터베이스를 삭제하고 다시 만듭니다. 자세한 내용은 다음 리소스를 참조하세요.

데이터베이스 삭제

SSOX(SQL Server 개체 탐색기)를 사용하여 데이터베이스를 삭제하거나, PMC(패키지 관리자 콘솔)에서 다음 명령을 실행합니다.

Drop-Database

초기 마이그레이션 만들기

PMC에서 다음 명령을 실행합니다.

Add-Migration InitialCreate
Update-Database

EnsureCreated 제거

이 자습서 시리즈는 EnsureCreated를 사용하여 시작되었습니다. EnsureCreated는 마이그레이션 기록 테이블을 만들지 않으므로 마이그레이션과 함께 사용할 수 없습니다. 데이터베이스를 삭제하고 자주 다시 생성하는 테스트 또는 신속한 프로토타입 만들기를 위해 디자인되었습니다.

이 지점부터 자습서에서는 마이그레이션을 사용합니다.

에서 Program.cs다음 줄을 삭제합니다.

context.Database.EnsureCreated();

앱을 실행하고 데이터베이스가 시드되는지 확인합니다.

Up 및 Down 메서드

EF Coremigrations add 명령은 데이터베이스를 만드는 코드를 생성했습니다. 이 마이그레이션 코드는 Migrations\<timestamp>_InitialCreate.cs 파일에 있습니다. InitialCreate 클래스의 Up 메서드는 데이터 모델 엔터티 집합에 해당하는 데이터베이스 테이블을 만듭니다. Down 메서드는 다음 예제처럼 테이블을 삭제합니다.

using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;

namespace ContosoUniversity.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Course",
                columns: table => new
                {
                    CourseID = table.Column<int>(nullable: false),
                    Title = table.Column<string>(nullable: true),
                    Credits = table.Column<int>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Course", x => x.CourseID);
                });

            migrationBuilder.CreateTable(
                name: "Student",
                columns: table => new
                {
                    ID = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    LastName = table.Column<string>(nullable: true),
                    FirstMidName = table.Column<string>(nullable: true),
                    EnrollmentDate = table.Column<DateTime>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Student", x => x.ID);
                });

            migrationBuilder.CreateTable(
                name: "Enrollment",
                columns: table => new
                {
                    EnrollmentID = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
                    CourseID = table.Column<int>(nullable: false),
                    StudentID = table.Column<int>(nullable: false),
                    Grade = table.Column<int>(nullable: true)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Enrollment", x => x.EnrollmentID);
                    table.ForeignKey(
                        name: "FK_Enrollment_Course_CourseID",
                        column: x => x.CourseID,
                        principalTable: "Course",
                        principalColumn: "CourseID",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_Enrollment_Student_StudentID",
                        column: x => x.StudentID,
                        principalTable: "Student",
                        principalColumn: "ID",
                        onDelete: ReferentialAction.Cascade);
                });

            migrationBuilder.CreateIndex(
                name: "IX_Enrollment_CourseID",
                table: "Enrollment",
                column: "CourseID");

            migrationBuilder.CreateIndex(
                name: "IX_Enrollment_StudentID",
                table: "Enrollment",
                column: "StudentID");
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Enrollment");

            migrationBuilder.DropTable(
                name: "Course");

            migrationBuilder.DropTable(
                name: "Student");
        }
    }
}

위의 코드는 초기 마이그레이션에 대한 코드입니다. 코드:

  • migrations add InitialCreate 명령에서 생성되었습니다.
  • database update 명령에서 실행됩니다.
  • 데이터베이스 컨텍스트 클래스에 지정된 데이터 모델의 데이터베이스를 만듭니다.

파일 이름에는 마이그레이션 이름 매개 변수(예제에서 InitialCreate)가 사용됩니다. 마이그레이션 이름은 임의의 유효한 파일 이름이 될 수 있습니다. 마이그레이션에서 수행 중인 작업을 요약한 단어 또는 구를 선택하는 것이 가장 좋습니다. 예를 들어 부서 테이블을 추가한 마이그레이션은 "AddDepartmentTable"이라고 할 수 있습니다.

마이그레이션 기록 테이블

  • SSOX 또는 SQLite 도구를 사용하여 데이터베이스를 검사합니다.
  • __EFMigrationsHistory 테이블이 추가된 것을 볼 수 있습니다. __EFMigrationsHistory 테이블은 데이터베이스에 적용된 마이그레이션을 추적합니다.
  • __EFMigrationsHistory 테이블의 데이터를 봅니다. 첫 번째 마이그레이션에 대해 한 행을 표시합니다.

데이터 모델 스냅샷

마이그레이션에서는 현재 데이터 모델의 Migrations/SchoolContextModelSnapshot.cs스냅샷 만듭니다. 마이그레이션을 추가하면 EF가 현재 데이터 모델을 스냅샷 파일과 비교하여 변경 내용을 확인합니다.

스냅샷 파일이 데이터 모델의 상태를 추적하므로 <timestamp>_<migrationname>.cs 파일을 삭제하여 마이그레이션을 삭제할 수 없습니다. 가장 최근 마이그레이션을 백업하려면 migrations remove 명령을 사용해야 합니다. migrations remove는 마이그레이션을 삭제하고 스냅샷이 올바르게 다시 설정되도록 합니다. 자세한 내용은 dotnet ef migrations remove를 참조하세요.

모든 마이그레이션을 제거하려면 모든 마이그레이션 초기화를 참조하세요.

프로덕션 환경에서 마이그레이션 적용

프로덕션 앱은 애플리케이션 시작 시 Database.Migrate를 호출하지 않는 것이 좋습니다. Migrate는 서버 팜에 배포된 앱에서 호출하면 안 됩니다. 앱이 여러 서버 인스턴스로 확장되는 경우 데이터베이스 스키마 업데이트가 여러 서버에서 발생하거나 읽기/쓰기 권한과 충돌하지 않도록 하는 것이 어렵습니다.

데이터베이스 마이그레이션은 배포의 일부로 제어된 방식으로 수행되어야 합니다. 프로덕션 데이터베이스 마이그레이션 방법은 다음과 같습니다.

  • 마이그레이션을 사용하여 SQL 스크립트를 작성하고 배포 시 SQL 스크립트 사용
  • 제어된 환경에서 dotnet ef database update 실행

문제 해결

앱에서 SQL Server LocalDB를 사용하고 다음 예외를 표시하는 경우:

SqlException: Cannot open database "ContosoUniversity" requested by the login.
The login failed.
Login failed for user 'user name'.

솔루션은 명령 프롬프트에서 dotnet ef database update를 실행해야 할 수 있습니다.

추가 리소스

다음 단계

다음 자습서에서는 데이터 모델을 빌드하여 엔터티 속성 및 새 엔터티를 추가합니다.

이 자습서에서는 데이터 모델 변경 관리를 위한 EF Core 마이그레이션 기능이 사용됩니다.

해결할 수 없는 문제가 발생한 경우 완성된 앱을 다운로드합니다.

새 앱이 개발되면 데이터 모델이 자주 변경됩니다. 모델이 변경될 때마다 모델이 데이터베이스와 동기화 해제됩니다. 이 자습서는 존재하지 않는 경우 데이터베이스를 만들도록 Entity Framework를 구성하여 시작되었습니다. 데이터 모델이 변경될 때마다 다음이 수행됩니다.

  • DB가 삭제됩니다.
  • EF는 모델과 일치하는 새 항목을 만듭니다.
  • 앱은 테스트 데이터로 DB를 시드합니다.

DB를 데이터 모델과 동기화된 상태로 유지하는 접근 방식은 앱을 제작 환경에 배포할 때까지 잘 작동하게 합니다. 앱이 프로덕션 환경에서 실행 중인 경우 일반적으로 유지 관리해야 하는 데이터를 저장합니다. 앱은 변경(예: 새 열 추가)될 때마다 테스트 DB로 시작될 수 없습니다. EF Core 마이그레이션 기능은 EF Core에서 새 데이터베이스를 만드는 대신 DB 스키마를 업데이트하도록 하여 이 문제를 해결합니다.

데이터 모델이 변경될 때 DB를 삭제하고 다시 작성하는 대신 마이그레이션은 스키마를 업데이트하고 기존 데이터를 유지합니다.

데이터베이스 삭제

SSOX(SQL Server 개체 탐색기) 또는 database drop 명령을 사용합니다.

PMC(패키지 관리자 콘솔)에서 다음 명령을 입력합니다.

Drop-Database

PMC에서 Get-Help about_EntityFrameworkCore를 실행하여 도움말 정보를 가져옵니다.

초기 마이그레이션 만들기 및 DB 업데이트

프로젝트를 빌드하고 첫 번째 마이그레이션을 만듭니다.

Add-Migration InitialCreate
Update-Database

Up 및 Down 메서드 검사

EF Coremigrations add 명령은 데이터베이스를 만드는 코드를 생성했습니다. 이 마이그레이션 코드는 Migrations\<timestamp>_InitialCreate.cs 파일에 있습니다. InitialCreate 클래스의 Up 메서드는 데이터 모델 엔터티 집합에 해당하는 데이터베이스 테이블을 만듭니다. Down 메서드는 다음 예제처럼 테이블을 삭제합니다.

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Course",
            columns: table => new
            {
                CourseID = table.Column<int>(nullable: false),
                Title = table.Column<string>(nullable: true),
                Credits = table.Column<int>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Course", x => x.CourseID);
            });

        migrationBuilder.CreateTable(
    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Enrollment");

        migrationBuilder.DropTable(
            name: "Course");

        migrationBuilder.DropTable(
            name: "Student");
    }
}

마이그레이션에서는 마이그레이션을 위한 데이터 모델 변경을 구현하기 위해 Up 메서드를 호출합니다. 업데이트를 롤백하는 명령을 입력하면 마이그레이션에서 Down 메서드를 호출합니다.

위의 코드는 초기 마이그레이션에 대한 코드입니다. 이 코드는 migrations add InitialCreate 명령을 실행할 때 만들어집니다. 파일 이름에는 마이그레이션 이름 매개 변수(이 예제에서 "InitialCreate")가 사용됩니다. 마이그레이션 이름은 임의의 유효한 파일 이름이 될 수 있습니다. 마이그레이션에서 수행 중인 작업을 요약한 단어 또는 구를 선택하는 것이 가장 좋습니다. 예를 들어 부서 테이블을 추가한 마이그레이션은 "AddDepartmentTable"이라고 할 수 있습니다.

초기 마이그레이션이 생성되었고 DB가 존재하는 경우:

  • DB 만들기 코드가 생성됩니다.
  • DB는 데이터 모델과 이미 일치하므로 DB 만들기 코드를 실행할 필요가 없습니다. DB 만들기 코드가 실행되면 DB가 데이터 모델과 이미 일치하므로 아무것도 변경되지 않습니다.

앱이 새 환경에 배포되면 DB 만들기 코드를 실행하여 DB를 만들어야 합니다.

이전에 DB는 삭제되었거나 존재하지 않으므로 마이그레이션에서 새 DB를 만듭니다.

데이터 모델 스냅샷

마이그레이션은 현재 데이터베이스 스키마의 스냅샷Migrations/SchoolContextModelSnapshot.cs에 만듭니다. 마이그레이션을 추가하면 EF가 데이터 모델을 스냅샷 파일과 비교하여 변경 내용을 확인합니다.

마이그레이션을 삭제하려면 다음 명령을 사용합니다.

마이그레이션 제거

마이그레이션 제거 명령은 마이그레이션을 삭제하고 스냅샷이 올바르게 다시 설정되도록 합니다.

EnsureCreated 제거 및 앱 테스트

초기 개발에는 EnsureCreated가 사용되었습니다. 이 자습서에서는 마이그레이션이 사용됩니다. EnsureCreated에는 다음과 같은 제한 사항이 적용됩니다.

  • 마이그레이션을 무시하고 DB 및 스키마를 만듭니다.
  • 마이그레이션 테이블을 만들지 않습니다.
  • 마이그레이션에 사용할 수 없습니다.
  • DB를 삭제하고 자주 다시 생성하는 테스트 또는 신속한 프로토타입 만들기를 위해 설계되었습니다.

EnsureCreated를 제거합니다.

context.Database.EnsureCreated();

앱을 실행하고 DB이 시드되었는지 확인합니다.

데이터베이스 검사

SQL Server 개체 탐색기를 사용하여 DB를 검사합니다. __EFMigrationsHistory 테이블이 추가된 것을 볼 수 있습니다. __EFMigrationsHistory 테이블은 DB에 적용된 마이그레이션을 추적합니다. __EFMigrationsHistory 테이블의 데이터를 보면 첫 번째 마이그레이션에 대해 한 행이 표시됩니다. 앞의 CLI 출력 예제의 마지막 로그는 이 행을 만드는 INSERT 문을 보여 줍니다.

앱을 실행하고 모든 항목이 작동하는지 확인합니다.

프로덕션 환경에서 마이그레이션 적용

프로덕션 앱은 애플리케이션 시작 시 Database.Migrate를 호출하지 않는 것이 좋습니다. 서버 팜의 앱에서 Migrate를 호출하면 안됩니다. 예를 들어, 앱이 스케일 아웃으로 클라우드에 배포된 경우(앱의 여러 인스턴스가 실행 중임)

데이터베이스 마이그레이션은 배포의 일부로 제어된 방식으로 수행되어야 합니다. 프로덕션 데이터베이스 마이그레이션 방법은 다음과 같습니다.

  • 마이그레이션을 사용하여 SQL 스크립트를 작성하고 배포 시 SQL 스크립트 사용
  • 제어된 환경에서 dotnet ef database update 실행

EF Core는 __MigrationsHistory 테이블을 사용하여 마이그레이션을 실행해야 하는지 확인합니다. DB가 최신 상태이면 마이그레이션이 실행되지 않습니다.

문제 해결

완료된 앱을 다운로드합니다.

앱에서는 다음과 같은 예외가 생성됩니다.

SqlException: Cannot open database "ContosoUniversity" requested by the login.
The login failed.
Login failed for user 'user name'.

해결 방법: dotnet ef database update를 실행합니다.

추가 리소스