흐름 API - 관계
참고 항목
이 페이지에서는 흐름 API를 사용하여 Code First 모델에서 관계를 설정하는 방법에 대한 정보를 제공합니다. EF의 관계 및 관계를 사용하여 데이터에 액세스하고 조작하는 방법에 대한 일반적인 내용은 관계 및 탐색 속성을 참조하세요.
Code First로 작업하는 경우 도메인 CLR 클래스를 정의하여 모델을 정의합니다. 기본적으로 Entity Framework는 Code First 규칙을 사용하여 클래스를 데이터베이스 스키마에 매핑합니다. Code First 명명 규칙을 사용하면 대부분의 경우 Code First를 사용하여 클래스에서 정의하는 외래 키와 탐색 속성을 기반으로 테이블 간의 관계를 설정할 수 있습니다. 클래스를 정의할 때 규칙을 따르지 않거나 규칙의 작동 방식을 변경하려는 경우 흐름 API 또는 데이터 주석을 사용하여 Code First가 테이블 간의 관계를 매핑할 수 있도록 클래스를 구성할 수 있습니다.
소개
흐름 API와의 관계를 구성할 때 EntityTypeConfiguration 인스턴스로 시작한 다음 HasRequired, HasOptional 또는 HasMany 메서드를 사용하여 이 엔터티가 참여하는 관계 유형을 지정합니다. HasRequired 및 HasOptional 메서드는 참조 탐색 속성을 나타내는 람다 식을 사용합니다. HasMany 메서드는 컬렉션 탐색 속성을 나타내는 람다 식을 사용합니다. 그런 다음 WithRequired, WithOptional 및 WithMany 메서드를 사용하여 역 탐색 속성을 구성할 수 있습니다. 이러한 메서드에는 인수를 사용하지 않는 오버로드가 있으며 이를 사용하여 단방향 탐색을 통해 카디널리티를 지정할 수 있습니다.
그런 다음 HasForeignKey 메서드를 사용하여 외래 키 속성을 구성할 수 있습니다. 이 메서드는 외래 키로 사용할 속성을 나타내는 람다 식을 사용합니다.
필수-선택적 관계 구성(일대0 또는 일대일)
다음 예제에서는 일대0 또는 일대일 관계를 구성합니다. OfficeAssignment에는 기본 키와 외래 키인 InstructorID 속성이 있는데, 이는 속성 이름이 HasKey 메서드가 기본 키를 구성하는 데 사용되는 규칙을 따르지 않기 때문입니다.
// Configure the primary key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
.HasKey(t => t.InstructorID);
// Map one-to-zero or one relationship
modelBuilder.Entity<OfficeAssignment>()
.HasRequired(t => t.Instructor)
.WithOptional(t => t.OfficeAssignment);
양쪽 끝이 필요한 관계 구성(일대일)
대부분의 경우 Entity Framework는 종속 형식과 관계의 보안 주체를 유추할 수 있습니다. 그러나 관계의 양쪽 끝이 필요하거나 양쪽이 선택적인 경우 Entity Framework는 종속 및 보안 주체를 식별할 수 없습니다. 관계의 양쪽 끝이 필요한 경우 HasRequired 메서드 다음에 WithRequiredPrincipal 또는 WithRequiredDependent를 사용합니다. 관계의 양쪽 끝이 선택적인 경우 HasOptional 메서드 다음에 WithOptionalPrincipal 또는 WithOptionalDependent를 사용합니다.
// Configure the primary key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
.HasKey(t => t.InstructorID);
modelBuilder.Entity<Instructor>()
.HasRequired(t => t.OfficeAssignment)
.WithRequiredPrincipal(t => t.Instructor);
다대다 관계 구성
다음 코드는 과정 형식 및 강사 유형 간에 다대다 관계를 구성합니다. 다음 예제에서는 기본 Code First 규칙을 사용하여 조인 테이블을 만듭니다. 결과적으로 CourseInstructor 테이블은 Course_CourseID 및 Instructor_InstructorID 열로 만들어집니다.
modelBuilder.Entity<Course>()
.HasMany(t => t.Instructors)
.WithMany(t => t.Courses)
테이블의 조인 테이블 이름과 열 이름을 지정하려면 Map 메서드를 사용하여 추가 구성을 수행해야 합니다. 다음 코드는 CourseID 및 InstructorID 열을 사용하여 CourseInstructor 테이블을 생성합니다.
modelBuilder.Entity<Course>()
.HasMany(t => t.Instructors)
.WithMany(t => t.Courses)
.Map(m =>
{
m.ToTable("CourseInstructor");
m.MapLeftKey("CourseID");
m.MapRightKey("InstructorID");
});
하나의 탐색 속성과 관계 구성
하나의 방향(단방향이라고도 함) 관계는 관계 끝 중 하나에만 탐색 속성이 정의되며 둘 다에 정의되지 않는 경우입니다. 규칙에 따라 Code First는 항상 단방향 관계를 일대다로 해석합니다. 예를 들어 강사 형식에만 탐색 속성이 있는 강사와 OfficeAssignment 간의 일대일 관계를 원하는 경우 흐름 API를 사용하여 이 관계를 구성해야 합니다.
// Configure the primary Key for the OfficeAssignment
modelBuilder.Entity<OfficeAssignment>()
.HasKey(t => t.InstructorID);
modelBuilder.Entity<Instructor>()
.HasRequired(t => t.OfficeAssignment)
.WithRequiredPrincipal();
계단식 삭제 사용
WillCascadeOnDelete 메서드를 사용하여 관계에 대한 계단식 삭제를 구성할 수 있습니다. 종속 엔터티의 외래 키가 null을 허용하지 않는 경우 Code First는 관계에 대한 계단식 삭제를 설정합니다. 종속 엔터티의 외래 키가 null을 허용하는 경우 Code First는 관계에 대한 계단식 삭제를 설정하지 않으며 보안 주체가 삭제되면 외래 키가 null로 설정됩니다.
다음을 사용하여 이러한 계단식 삭제 규칙을 제거할 수 있습니다.
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>()
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>()
다음 코드는 필요한 관계를 구성한 다음 계단식 삭제를 사용하지 않도록 설정합니다.
modelBuilder.Entity<Course>()
.HasRequired(t => t.Department)
.WithMany(t => t.Courses)
.HasForeignKey(d => d.DepartmentID)
.WillCascadeOnDelete(false);
복합 외래 키 구성
부서 형식의 기본 키가 DepartmentID 및 Name 속성으로 구성된 경우 다음과 같이 부서의 기본 키와 과정 형식의 외래 키를 구성합니다.
// Composite primary key
modelBuilder.Entity<Department>()
.HasKey(d => new { d.DepartmentID, d.Name });
// Composite foreign key
modelBuilder.Entity<Course>()
.HasRequired(c => c.Department)
.WithMany(d => d.Courses)
.HasForeignKey(d => new { d.DepartmentID, d.DepartmentName });
모델에 정의되지 않은 외래 키 이름 바꾸기
CLR 형식에서 외래 키를 정의하지 않고 데이터베이스에 포함할 이름을 지정하려는 경우 다음을 수행합니다.
modelBuilder.Entity<Course>()
.HasRequired(c => c.Department)
.WithMany(t => t.Courses)
.Map(m => m.MapKey("ChangedDepartmentID"));
Code First 규칙을 따르지 않는 외래 키 이름 구성
Course 클래스의 외래 키 속성을 DepartmentID 대신 SomeDepartmentID라고 하는 경우 다음을 수행하여 SomeDepartmentID를 외래 키로 지정해야 합니다.
modelBuilder.Entity<Course>()
.HasRequired(c => c.Department)
.WithMany(d => d.Courses)
.HasForeignKey(c => c.SomeDepartmentID);
샘플에 사용되는 모델
다음 Code First 모델은 이 페이지의 샘플에 사용됩니다.
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
// add a reference to System.ComponentModel.DataAnnotations DLL
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System;
public class SchoolEntities : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure Code First to ignore PluralizingTableName convention
// If you keep this convention then the generated tables will have pluralized names.
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
public class Department
{
public Department()
{
this.Courses = new HashSet<Course>();
}
// Primary key
public int DepartmentID { get; set; }
public string Name { get; set; }
public decimal Budget { get; set; }
public System.DateTime StartDate { get; set; }
public int? Administrator { get; set; }
// Navigation property
public virtual ICollection<Course> Courses { get; private set; }
}
public class Course
{
public Course()
{
this.Instructors = new HashSet<Instructor>();
}
// Primary key
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
// Foreign key
public int DepartmentID { get; set; }
// Navigation properties
public virtual Department Department { get; set; }
public virtual ICollection<Instructor> Instructors { get; private set; }
}
public partial class OnlineCourse : Course
{
public string URL { get; set; }
}
public partial class OnsiteCourse : Course
{
public OnsiteCourse()
{
Details = new Details();
}
public Details Details { get; set; }
}
public class Details
{
public System.DateTime Time { get; set; }
public string Location { get; set; }
public string Days { get; set; }
}
public class Instructor
{
public Instructor()
{
this.Courses = new List<Course>();
}
// Primary key
public int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public System.DateTime HireDate { get; set; }
// Navigation properties
public virtual ICollection<Course> Courses { get; private set; }
}
public class OfficeAssignment
{
// Specifying InstructorID as a primary
[Key()]
public Int32 InstructorID { get; set; }
public string Location { get; set; }
// When Entity Framework sees Timestamp attribute
// it configures ConcurrencyCheck and DatabaseGeneratedPattern=Computed.
[Timestamp]
public Byte[] Timestamp { get; set; }
// Navigation property
public virtual Instructor Instructor { get; set; }
}
.NET