자습서: ASP.NET MVC 5 앱에서 EF를 사용하여 상속 구현

이전 자습서에서는 동시성 예외를 처리했습니다. 이 자습서에서는 데이터 모델에서 상속을 구현하는 방법을 보여 줍니다.

개체 지향 프로그래밍에서 상속 을 사용하여 코드 재사용을 용이하게 할 수 있습니다. 이 자습서에서는 강사와 학생 모두에게 공통적인 속성(예: LastName)이 포함된 Person 기본 클래스에서 클래스가 파생되도록 InstructorStudent 클래스를 변경합니다. 웹 페이지를 추가하거나 변경하지는 않지만 일부 코드를 변경하고 이러한 변경 내용이 데이터베이스에 자동으로 반영됩니다.

이 자습서에서는 다음과 같은 작업을 수행합니다.

  • 데이터베이스에 상속을 매핑하는 방법 알아보기
  • Person 클래스 만들기
  • 강사 및 학생 업데이트
  • 모델에 사람 추가
  • 마이그레이션 만들기 및 업데이트
  • 구현 테스트
  • Azure에 배포

사전 요구 사항

데이터베이스에 상속 매핑

Instructor 데이터 모델의 및 Student 클래스 School 에는 동일한 여러 속성이 있습니다.

Student_and_Instructor_classes

InstructorStudent 엔터티에서 공유하는 속성에 대해 중복 코드를 제거하려고 한다고 가정해 보겠습니다. 또는 강사 또는 학생의 이름인지 여부를 신경쓰지 않고 이름의 형식을 지정할 수 있는 서비스를 작성하려고 합니다. 다음 그림과 같이 공유 속성만 포함하는 기본 클래스를 만든 Person 다음, 및 Student 엔터티를 해당 기본 클래스에서 상속할 Instructor 수 있습니다.

Student_and_Instructor_classes_deriving_from_Person_class

데이터베이스에 이 상속 구조를 여러 가지 방법으로 나타낼 수 있습니다. 학생과 강사에 관한 정보를 하나의 테이블에 포함하는 Person 테이블을 포함할 수 있습니다. 일부 열은 강사(HireDate)에만 적용할 수 있으며 일부는 학생(EnrollmentDate)에게만 적용되며 일부는 둘 다(LastName, FirstName)에만 적용될 수 있습니다. 일반적으로 각 행이 나타내는 형식을 나타내는 판별 자 열이 있습니다. 예를 들어, 판별자 열은 강사에 대해 "Instructor"를, 학생에 대해 "Student"를 포함할 수 있습니다.

테이블당 hierarchy_example

단일 데이터베이스 테이블에서 엔터티 상속 구조를 생성하는 이 패턴을 TPH( 테이블 단위 계층 ) 상속이라고 합니다.

다른 방법은 데이터베이스를 상속 구조와 유사하게 만드는 것입니다. 예를 들어 Person 테이블에 이름 필드만 있고 날짜 필드가 있는 별도의 InstructorStudent 테이블을 포함할 수 있습니다.

테이블별 type_inheritance

각 엔터티 클래스에 대한 데이터베이스 테이블을 만드는 이 패턴을 TPT( 형식별 테이블 ) 상속이라고 합니다.

또 다른 옵션은 모든 비추상 형식을 개별 테이블에 매핑하는 것입니다. 상속된 속성을 포함한 모든 클래스 속성을 해당 테이블의 열에 매핑합니다. 이 패턴을 TPC(구체적 클래스당 하나의 테이블) 상속이라고 합니다. 앞에서 표시된 것처럼 Person, StudentInstructor 클래스에 대한 TPC 상속을 구현한 경우, StudentInstructor 테이블은 상속을 구현한 후에도 이전과 다르지 않습니다.

TPC 및 TPH 상속 패턴은 일반적으로 TPT 상속 패턴보다 Entity Framework에서 더 나은 성능을 제공합니다. TPT 패턴으로 인해 복잡한 조인 쿼리가 발생할 수 있기 때문입니다.

이 자습서에서는 TPH 상속을 구현하는 방법을 보여 줍니다. TPH는 Entity Framework의 기본 상속 패턴이므로 클래스를 만들고Person, 에서 파생Person되도록 및 클래스를 변경 Instructor 하고Student, 에 새 클래스를 DbContext추가하고, 마이그레이션을 만들기만 하면 됩니다. (다른 상속 패턴을 구현하는 방법에 대한 자세한 내용은 MSDN Entity Framework 설명서 에서 TPT(Table-Per-Type) 상속 매핑TPC(Table-Per-Concrete Class) 상속 매핑 을 참조하세요.)

Person 클래스 만들기

Models 폴더에서 Person.cs를 만들고 템플릿 코드를 다음 코드로 바꿉니다.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public abstract class Person
    {
        public int ID { get; set; }

        [Required]
        [StringLength(50)]
        [Display(Name = "Last Name")]
        public string LastName { get; set; }
        [Required]
        [StringLength(50, ErrorMessage = "First name cannot be longer than 50 characters.")]
        [Column("FirstName")]
        [Display(Name = "First Name")]
        public string FirstMidName { get; set; }

        [Display(Name = "Full Name")]
        public string FullName
        {
            get
            {
                return LastName + ", " + FirstMidName;
            }
        }
    }
}

강사 및 학생 업데이트

이제 Person.sc 값을 상속하도록 Instructor.csStudent.cs를 업데이트합니다.

Instructor.cs에서 Person 클래스를 Instructor 파생하고 키 및 이름 필드를 제거합니다. 해당 코드는 다음 예제와 같이 나타납니다.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Instructor : Person
    {
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        [Display(Name = "Hire Date")]
        public DateTime HireDate { get; set; }

        public virtual ICollection<Course> Courses { get; set; }
        public virtual OfficeAssignment OfficeAssignment { get; set; }
    }
}

Student.cs를 비슷하게 변경합니다. 클래스는 Student 다음 예제와 같습니다.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Student : Person
    {
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        [Display(Name = "Enrollment Date")]
        public DateTime EnrollmentDate { get; set; }

        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
}

모델에 사람 추가

SchoolContext.cs에서 엔터티 형식에 Person 대한 속성을 추가 DbSet 합니다.

public DbSet<Person> People { get; set; }

계층당 하나의 테이블 상속을 구성하기 위해 Entity Framework에 필요한 모든 작업입니다. 보듯이 데이터베이스가 업데이트되면 PersonInstructor 테이블 대신 테이블이 Student 있습니다.

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

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

Add-Migration Inheritance

Update-Database PMC에서 명령을 실행합니다. 마이그레이션에서 처리하는 방법을 모르는 기존 데이터가 있으므로 이 시점에서 명령이 실패합니다. 다음과 같은 오류 메시지가 표시됩니다.

개체 'dbo를 삭제할 수 없습니다. 강사'는 FOREIGN KEY 제약 조건에서 참조되기 때문입니다.

마이그레이션<를 엽니다. timestamp>_Inheritance.cs 및 메서드를 Up 다음 코드로 바꿉니다.

public override void Up()
{
    // Drop foreign keys and indexes that point to tables we're going to drop.
    DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
    DropIndex("dbo.Enrollment", new[] { "StudentID" });

    RenameTable(name: "dbo.Instructor", newName: "Person");
    AddColumn("dbo.Person", "EnrollmentDate", c => c.DateTime());
    AddColumn("dbo.Person", "Discriminator", c => c.String(nullable: false, maxLength: 128, defaultValue: "Instructor"));
    AlterColumn("dbo.Person", "HireDate", c => c.DateTime());
    AddColumn("dbo.Person", "OldId", c => c.Int(nullable: true));

    // Copy existing Student data into new Person table.
    Sql("INSERT INTO dbo.Person (LastName, FirstName, HireDate, EnrollmentDate, Discriminator, OldId) SELECT LastName, FirstName, null AS HireDate, EnrollmentDate, 'Student' AS Discriminator, ID AS OldId FROM dbo.Student");

    // Fix up existing relationships to match new PK's.
    Sql("UPDATE dbo.Enrollment SET StudentId = (SELECT ID FROM dbo.Person WHERE OldId = Enrollment.StudentId AND Discriminator = 'Student')");

    // Remove temporary key
    DropColumn("dbo.Person", "OldId");

    DropTable("dbo.Student");

    // Re-create foreign keys and indexes pointing to new table.
    AddForeignKey("dbo.Enrollment", "StudentID", "dbo.Person", "ID", cascadeDelete: true);
    CreateIndex("dbo.Enrollment", "StudentID");
}

이 코드는 다음과 같은 데이터베이스 업데이트 작업을 처리합니다.

  • Student 테이블을 가리키는 외래 키 제약 조건 및 인덱스를 제거합니다.

  • Instructor 테이블 이름을 Person으로 바꾸고 Student 데이터를 저장하기 위해 필요한 변경을 수행합니다.

    • 학생에 대한 Nullable EnrollmentDate를 추가합니다.
    • 행이 학생용인지, 강사용인지 나타내는 판별자 열을 추가합니다.
    • 학생 행은 고용 날짜를 포함하지 않으므로 HireDate를 Nullable로 설정합니다.
    • 학생을 가리키는 외래 키를 업데이트하는 데 사용할 임시 필드를 추가합니다. 학생을 Person 테이블에 복사하면 새 기본 키 값이 표시됩니다.
  • Student 테이블에서 Person 테이블로 데이터를 복사합니다. 그러면 학생에게 새 기본 키 값이 할당됩니다.

  • 학생을 가리키는 외래 키 값을 수정합니다.

  • 이제 Person 테이블을 가리키도록 외래 키 제약 조건 및 인덱스를 다시 만듭니다.

(기본 키 형식으로 정수 대신 GUID를 사용한 경우, 학생 기본 키 값을 변경할 필요가 없으며 이 단계 중 일부가 생략되었을 수 있습니다.)

update-database 명령을 다시 실행합니다.

(프로덕션 시스템에서는 이전 데이터베이스 버전으로 돌아가기 위해 이 메서드를 사용해야 하는 경우를 대비하여 Down 메서드를 해당 변경합니다. 이 자습서에서는 Down 메서드를 사용하지 않습니다.)

참고

데이터를 마이그레이션하고 스키마를 변경할 때 다른 오류가 발생할 수 있습니다. 마이그레이션 오류를 resolve 수 없는 경우 Web.config파일에서 연결 문자열 변경하거나 데이터베이스를 삭제하여 자습서를 계속할 수 있습니다. 가장 간단한 방법은 Web.config 파일에서 데이터베이스의 이름을 바꾸는 것입니다. 예를 들어 다음 예제와 같이 데이터베이스 이름을 ContosoUniversity2로 변경합니다.

<add name="SchoolContext" 
    connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity2;Integrated Security=SSPI;" 
    providerName="System.Data.SqlClient" />

새 데이터베이스를 사용하면 마이그레이션할 데이터가 없으며 명령이 update-database 오류 없이 완료될 가능성이 훨씬 더 높습니다. 데이터베이스를 삭제하는 방법에 대한 지침은 Visual Studio 2012에서 데이터베이스를 삭제하는 방법을 참조하세요. 자습서를 계속 진행하기 위해 이 방법을 사용하는 경우 이 자습서의 끝에 있는 배포 단계를 건너뛰거나 새 사이트 및 데이터베이스에 배포합니다. 이미 배포한 동일한 사이트에 업데이트를 배포하는 경우 EF는 마이그레이션을 자동으로 실행할 때 동일한 오류가 발생합니다. 마이그레이션 오류를 해결하려는 경우 가장 좋은 리소스는 Entity Framework 포럼 또는 StackOverflow.com 중 하나입니다.

구현 테스트

사이트를 실행하고 다양한 페이지를 사용해 보세요. 모든 항목이 이전과 같이 작동합니다.

서버 Explorer데이터 연결\SchoolContext를 확장한 다음 테이블을 확장하면 StudentInstructor 테이블이 Person 테이블로 대체된 것을 볼 수 있습니다. Person 테이블을 확장하면 학생강사 테이블에 있는 모든 열이 표시됩니다.

Person 테이블을 마우스 오른쪽 단추로 클릭한 후 테이블 데이터 표시를 클릭하여 판별자 열을 표시합니다.

다음 다이어그램에서는 새 School 데이터베이스의 구조를 보여 줍니다.

School_database_diagram

Azure에 배포

이 섹션에서는 이 자습서 시리즈의 3부, 정렬, 필터링 및 페이징에서 선택적 Azure에 앱 배포 섹션을 완료해야 합니다. 로컬 프로젝트에서 데이터베이스를 삭제하여 해결한 마이그레이션 오류가 있는 경우 이 단계를 건너뜁니다. 또는 새 사이트 및 데이터베이스를 만들고 새 환경에 배포합니다.

  1. Visual Studio의 솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 상황에 맞는 메뉴에서 게시를 선택합니다.

  2. 게시를 클릭합니다.

    웹앱이 기본 브라우저에서 열립니다.

  3. 애플리케이션을 테스트하여 작동하는지 확인합니다.

    데이터베이스에 액세스하는 페이지를 처음 실행할 때 Entity Framework는 현재 데이터 모델을 사용하여 데이터베이스를 최신 상태로 만드는 데 필요한 모든 마이그레이션 Up 메서드를 실행합니다.

코드 가져오기

완료된 프로젝트 다운로드

추가 리소스

다른 Entity Framework 리소스에 대한 링크는 ASP.NET 데이터 액세스 - 권장 리소스에서 찾을 수 있습니다.

이 상속 구조 및 기타 상속 구조에 대한 자세한 내용은 MSDN 의 TPT 상속 패턴TPH 상속 패턴을 참조하세요. 다음 자습서에서는 다양한 고급 Entity Framework 시나리오를 처리하는 방법을 살펴봅니다.

다음 단계

이 자습서에서는 다음을 수행합니다.

  • 상속을 데이터베이스에 매핑하는 방법 알아보기
  • Person 클래스 만들기
  • 강사 및 학생 업데이트
  • 모델에 사람 추가
  • 마이그레이션 만들기 및 업데이트
  • 구현 테스트
  • Azure에 배포됨

다음 문서로 이동하여 Entity Framework Code First를 사용하는 ASP.NET 웹 애플리케이션 개발의 기본 사항을 넘어서는 경우에 유의해야 하는 topics 대해 알아봅니다.